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

本文介绍了在MVVM模式下如何在WPF应用中关闭子窗口。需求是在主窗口打开子窗口执行数据操作,保存后自动关闭。由于MVVM模式下不能直接使用路由事件,提出了两种解决方案:1. 通过Binding绑定,在子窗口的ViewModel中处理关闭操作;2. 在ViewModel中实现路由事件。文章详细展示了代码实现,并提供了查找父级窗口的辅助方法。
摘要由CSDN通过智能技术生成

离上一篇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;
        }
}

注重小细节,掌握大知识

WPF MVVM模式下,窗口的ShowDialog实现是通过使用自定义的对话框服务来实现的。 首先,我们需要创建一个实现了IDialogService接口的自定义对话框服务类,该接口定义了ShowDialog方法。例如: ``` public interface IDialogService { bool? ShowDialog(object viewModel); } ``` 然后,在实际的对话框服务类中,我们可以使用WPF的Window或者其他自定义的Window来实现ShowDialog方法。在ShowDialog方法内部,我们可以根据传入的ViewModel来创建对应的视图,并将ViewModel与视图进行关联。例如: ``` public class DialogService : IDialogService { public bool? ShowDialog(object viewModel) { var dialogWindow = new DialogWindow(); dialogWindow.DataContext = viewModel; return dialogWindow.ShowDialog(); } } ``` 在ViewModel中,我们需要使用对话框服务来调用ShowDialog方法,并将当前的视图模型作为参数传入。在调用ShowDialog方法后,可以根据返回值来判断对话框是通过"确定"按钮还是"取消"按钮关闭的。例如: ``` public class MainViewModel { private readonly IDialogService _dialogService; public MainViewModel(IDialogService dialogService) { _dialogService = dialogService; } public void OpenDialog() { var dialogViewModel = new DialogViewModel(); var result = _dialogService.ShowDialog(dialogViewModel); if (result == true) { // 确定按钮被点击 } else if (result == false) { // 取消按钮被点击 } } } ``` 最后,在使用MVVM模式的主视图或者其他视图中,我们可以通过依赖注入或者其他方式来创建对话框服务的实例,并将其作为参数传入ViewModel的构造函数中,以便于在ViewModel中调用ShowDialog方法来展示对话框。例如: ``` public partial class MainWindow : Window { private readonly MainViewModel _viewModel; public MainWindow() { InitializeComponent(); var dialogService = new DialogService(); _viewModel = new MainViewModel(dialogService); DataContext = _viewModel; } private void OpenDialogButton_Click(object sender, RoutedEventArgs e) { _viewModel.OpenDialog(); } } ``` 通过以上步骤,我们就可以实现在WPF MVVM模式窗口ShowDialog的功能。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值