WPF:从MVVM模型关闭窗口
问题的原因是在MVVM模式中,ViewModel无法直接关闭窗口,因为ViewModel和View之间没有直接的联系。解决方法是让ViewModel来关闭窗口,通过在ViewModel中传递信息给Model,然后判断是否有有效数据,如果没有则关闭窗口。在MVVM中,一个空的View表示没有数据的正常方式。如果需要在ViewModel中关闭窗口,可以参考这篇文章:asimsajjad.blogspot.de/2010/10/…。
问题:WPF中,如何从Model MVVM中关闭一个Window?
原因:在View Model中引用View不是最佳实践。因为当你对View Model进行单元测试时,需要实例化View。对于小的View来说,这可能不难,但是对于具有复杂依赖树的复杂View来说,这并不好。
解决方法:我个人认为,与View进行通信的最简单方法是通过在ViewModel构造函数中传递IInputElement。IInputElement的好处是它具有路由事件的支持,它具有RaiseEvent和AddHandler方法,用于路由事件。因此,您可以自由地将事件冒泡/隧道/直接传递给应用程序中的任何View或ViewModel,而无需使用任何附加库。
以下是我在ViewModel中的简化代码,但请记住,这种技术只适用于View First的方法:
public class MyViewModel : INotifyPropertyChanged { public static readonly RoutedEvent RequestCloseEvent = EventManager.RegisterRoutedEvent("RequestClose", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(MyViewModel)); private IInputElement dispatcher; public MyViewModel(IInputElement dispatcher) { this.dispatcher = dispatcher; } public void CloseApplication() { dispatcher.RaiseEvent(new RoutedEventArgs(RequestCloseEvent)); } }
在您的View中简单地使用以下代码:
DataContext = new MyViewModel(this); //注意构造函数中的"this"
在您应用程序的根视图(Window)中简单地使用以下代码:
public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); AddHandler(MyViewModel.RequestCloseEvent, new RoutedEventHandler(onRequestClose)); } private void onRequestClose(object sender, RoutedEventArgs e) { if (MessageBox.Show("Are you sure you want to quit?", "Confirmation", MessageBoxButton.YesNo) == MessageBoxResult.Yes) { Close(); } } }
由于IInputElement是接口而不是类,您可以轻松地为您的单元测试创建一个模拟类:
var target = new MyViewModel(new DispatcherMock);
或者您可以使用像RhinoMocks这样的模拟库。
进一步阅读,您可以了解如何使用Routed Event。
WPF中的MVVM模式中,ViewModels不应该以任何方式引用View,包括关闭窗口。相反,View和ViewModel之间的通信通常通过某种事件或消息系统进行,例如Microsoft Prism的EventAggregator或MVVM Light的Messenger。
例如,View应该订阅监听CloseWindow类型的事件消息,当接收到这样的消息时,View应该关闭自己。然后,ViewModel只需要在想要告诉View关闭时广播一个CloseWindow消息。
如果您感兴趣,我在我的博客文章《Communication between ViewModels》中对MVVM中的事件系统进行了简要概述,并提供了一些示例。