欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

WPF随笔(十四)--如何在MVVM模式下关闭窗口

程序员文章站 2022-03-07 14:18:36
...

离上一篇WPF随笔有多久,再度编码WPF项目就有多久。机缘巧合又接下了一个开发WPF桌面程序的任务,又有机会详细研究之前一直忽略的细节。
今天就来谈谈如何在MVVM模式下关闭窗口。
什么?关闭窗口还要写代码?点个×不就行了?
起初我也是这么想的, 然而实践证明并没有那么简单。


1.需求场景描述

在主窗口(一般默认是MainWindow)打开子窗口ChildWindow,在子窗口中进行数据的新增或编辑操作,点击自定义的“保存”按钮,在数据保存完成后自动关闭当前子窗口。
需求非常简单,如果使用路由事件那将会非常简单。但使用MVVM模式就意味着View视图层与ViewModel视图模型层的分离,直接添加路由事件不太现实。

2.解决方案

通用的解决方案有很多,网上一搜一大堆,大体思路都一样。结合MVVM模式的思想和WPF的自身特性,一是从Binding绑定着手,二是不能在xaml.cs里写路由事件就在ViewModel里实现路由事件。

2.1 从绑定着手

子窗口ChildWindow的xaml代码片段

<Button Command="{Binding SaveCommand}" CommandParameter="{Binding RelativeSource={RelativeSource FindAncestor,AncestorType={x:Type Window}}}">
    <Button.ContentTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal" VerticalAlignment="Center" HorizontalAlignment="Center">
                <icon:PackIconModern Width="12" Height="12" HorizontalAlignment="Center" VerticalAlignment="Center" Kind="Save"></icon:PackIconModern>
                <TextBlock Margin="3,0,0,0">保存</TextBlock>
            </StackPanel>
        </DataTemplate>
    </Button.ContentTemplate>
</Button>

其中绑定参数CommandParameter是重点,表达式的含义是找到当前的窗口。

ViewModel层ChildViewModel.cs的命令定义:

 //保存命令,传递参数为窗口类型
 public DelegateCommands<System.Windows.Window> SaveCommand { get; set; }

ChildViewModel.cs的命令对应方法实现

 private async void Save(System.Windows.Window obj)
 {
 	///
 	///你的业务逻辑代码
 	///
 	
    var win = obj;
    win.Close();

 }

2.2 从路由事件着手

MVVM模式下在xaml.cs里面写路由事件那味道就不对了,但是ViewModel层也可以写代码实现路由事件。
ChildViewModel.cs里的代码片段:

public ChildViewModel()
{
	//你的初始化代码

    System.Windows.EventManager.RegisterClassHandler(typeof(System.Windows.Controls.Button), System.Windows.Controls.Button.ClickEvent, new System.Windows.RoutedEventHandler(SaveButtonClicked));

}

private void SaveButtonClicked(object sender, RoutedEventArgs e)
{
     ///你的业务逻辑代码
     
     System.Windows.Controls.Button btn = (System.Windows.Controls.Button)e.Source;
     var win = FindVisualParent<Window>(btn)[0];
     win.Close();
}

其中通过可视化数据查找指定父级元素的方法为:

/// 利用VisualTreeHelper寻找指定依赖对象的父级对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="obj"></param>
/// <returns></returns>
public static List<T> FindVisualParent<T>(DependencyObject obj) where T : DependencyObject
{
    try
    {
        List<T> TList = new List<T> { };
        DependencyObject parent = VisualTreeHelper.GetParent(obj);
        if (parent != null && parent is T)
        {
            TList.Add((T)parent);
            List<T> parentOfParent = FindVisualParent<T>(parent);
            if (parentOfParent != null)
            {
                TList.AddRange(parentOfParent);
            }
        }
        else if (parent != null)
        {
            List<T> parentOfParent = FindVisualParent<T>(parent);
            if (parentOfParent != null)
            {
                TList.AddRange(parentOfParent);
            }
        }
            return TList;
       }
        catch (Exception)
        {
            return null;
        }
}

注重小细节,掌握大知识