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

离上一篇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寻找指定依赖对象的父级对象
/// 
/// 
/// 
/// 
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;
        }
}

注重小细节,掌握大知识

你可能感兴趣的:(WPF,wpf,mvvm,窗口管理)