65.9K
CodeProject 正在变化。 阅读更多。
Home

WPF,SDI 链接窗口基类或不要错过您的窗口

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.50/5 (2投票s)

2013 年 2 月 3 日

CPOL

3分钟阅读

viewsIcon

19021

downloadIcon

391

一个用于控制打开窗口的简单类。

引言

当您使用 WPF、MVVM 和 SDI 窗口时,一个常见的行为是当您关闭主窗口时,您很容易失去对窗口的控制。也就是说,子窗口在您关闭父窗口时仍然打开,您需要单独关闭它们。要控制这一点,您需要创建自定义逻辑来在适当的时候关闭子窗口。本文介绍了一种实现此目的的方法,并提供了一个基类,该类为您实现了控制窗口的逻辑。

背景

这里用于控制窗口的逻辑基于基视图模型类中的两个属性。一个属性保留对父窗口的视图和视图模型以及实际视图模型的视图和视图模型的引用。此属性的类型为 ILinked。请参见图片。

另一个是 ILinked 对象的列表,包含相同的信息,但来自为实际视图模型创建的子窗口。

这些属性中存储的信息使您有机会在关闭父窗口时关闭所有子窗口,并在关闭子窗口时通知父窗口。

这两个描述的属性在 LinkedViewModel 类中实现,您应该让您的视图模型继承自 LinkedViewModel。这个类拥有您自动管理子窗口状态并向现有父窗口报告其自身状态所需的一切。

每次创建窗口时,都会创建一个关于可能子窗口的 ILinked 信息列表,并使用关于窗口的必要信息填充 LinkedValues。如果您创建一个新窗口,新窗口会创建其适当的 LinkedValues 属性并将其添加到父窗口的子列表中。

所有控制逻辑都在构造函数中调用,并发生在以下过程中

/// <summary>
/// Internal refactoring constructor code.
/// </summary>
/// <param name="view" >
/// The view for this view model.
/// </param>
/// <param name="parentView" />
/// The parent view.
/// </param>
/// <param name="parentViewModel" >
/// The parent view model.
/// </param>
private void SetParentValue(ContentControl view, ContentControl parentView, 
        ILinkedViewModel parentViewModel)
{
    this.ChildControlList = new List<ilinked>();
    ILinked ntc = Linked.Factory();
    ntc.ViewModel = this;
    ntc.ViewModelParent = parentViewModel;
    ntc.ViewParent = parentView;
    ntc.View = view;
    this.LinkedValues = ntc;

    // Set the child list of the parent, if it is not the root windows.
    if (parentViewModel != null)
    {
        if (parentViewModel.ChildControlList != null)
        {
            parentViewModel.ChildControlList.Add(ntc);
        }
    }

    // Set to get the closed envent.
    if (view is Window)
    {
        ((Window)view).Closed += this.TracingViewModelClosed;
    }
}

请注意,它还为 Closed 窗口事件创建一个事件。此事件用于从父子列表中删除 Linked 实例,正如您在以下代码中看到的那样

/// <summary>
/// Performs application-defined tasks associated with freeing, releasing, 
/// or resetting unmanaged resources.
/// </summary>
public void Dispose()
{
   var copyList = new List<ilinked>();
   copyList.AddRange(this.ChildControlList);

   foreach (ILinked tc in copyList)
   {
       var view = tc.View;
       if (view is Window)
       {
            ((Window)view).Close();
       }
   }

   // Disconnect from the parent
   if (this.LinkedValues != null && this.LinkedValues.ViewModelParent != null)
   {
       var vmp = this.LinkedValues.ViewModelParent;
       vmp.ChildControlList.Remove(this.LinkedValues);
   }
}

如果一个窗口被关闭,它会自动关闭 ChildControlList 中存在的所有子窗口,并从父窗口的 ChildControlList 中删除关于它的信息。

然后窗口的控制由基类自动操作,您不需要创建代码来执行此操作。呈现的 LinkedViewModel 还实现了 INotifyPropertyChanges 以支持 MVVM 对属性的支持。

使用代码

要使用这些类,只需从 LinkedViewModel 继承您的视图模型,如以下代码所示。

/// <summary>
/// Base class for ViewModels.
/// </summary>
public class UserRootViewModel : LinkedViewModel
{
 ...
}

要创建您的根视图模型类和窗口,只需在项目的 App.axml.cs 中声明它,并使用以下代码创建

/// <summary>
/// Interaction logic for App.xaml
/// </summary>
public partial class App
{
    /// <summary>
    /// Main Windows.
    /// </summary>
    private readonly Window main;

    /// <summary>
    /// ViewModel for tracing Windows.
    /// </summary>
    private readonly LinkedViewModel vm;

    /// <summary>
    /// Initializes a new instance of the <see cref="App"> class.
    /// </summary>
    public App()
    {
        this.main = new UserRootView();
        this.vm = new UserRootViewModel(this.main, null, null);
        this.main.DataContext = this.vm;
        this.main.Show();
    }
}

在这种情况下,我们将父视图和视图模型设置为 null,因为它们不存在。

要创建子窗口,只需将新窗口的视图模型声明为根类并创建您的视图。然后在同一表单中创建,但在这种情况下填充父参数。请参阅以下示例代码

/// <summary>
/// Command to create multiple windows.
/// </summary>
private void CreateMultipleWindow()
{
    var view = new WindowsChild();
    var viewModel = new WindowsChildViewModel(view, this.LinkedValues.View, this);
    view.DataContext = viewModel;
    view.Show();
}

您还可以使用 LinkedViewModel 的实例控制来查看您想要创建的窗口是否有更多实例存在。此辅助过程可帮助您限制需要同时创建的窗口数量。要使用此功能,您只需调用静态方法 TestMultiplicyOverrun。请参阅以下示例

/// <summary>
/// Create a single windows.
/// </summary>
private void CreateSingleWindow()
{
 var view = new WindowsChild();
 if (LinkedViewModel.TestMultiplicyOverrun(view, this, 1))
 {
    view.Close();
    return;
 }

  var viewModel = new WindowsChildViewModel(view, this.LinkedValues.View, this);
  view.DataContext = viewModel;
  view.Show();
}

该例程只是扫描视图模型中的子列表,如果相同类型的活动窗口数量大于允许的窗口数量(该方法的最后一个参数),则返回 true。

请参阅代码以获取完整的示例。该示例还支持 MVVM,以说明具有该模式的完整应用程序。

关注点

本文提供了一种在 SDI 应用程序中使用 WPF 和 MVVM 控制子窗口的简单方法。

历史

  • First version.
© . All rights reserved.