我正在尝试从其 ViewModel 关闭一个窗口。我正在使用 MVVM 模式。我已经厌倦了使用窗户;
Window parentWindow = Window.GetWindow(this);
但我不能这样做,我如何获得 ViewModel 的窗口,以便我能够关闭窗口。我希望能够在代码中做到这一点。
你能在代码中找到父窗口吗?
ViewModels
不应View
以任何方式引用 MVVM,包括关闭窗口。
相反,View
和之间的通信ViewModel
通常是通过某种事件或消息系统完成的,例如Microsoft PrismEventAggregator
或MVVM Light 的Messenger
例如,View
应该订阅侦听类型的事件消息CloseWindow
,并且当它收到这些消息之一时,它应该关闭自己。然后,只要它想告诉它关闭,它就必须随时ViewModel
广播一条消息。CloseWindow
View
如果您有兴趣,可以在我关于ViewModel 之间的通信的博客文章中简要概述 MVVM 中的事件系统和一些示例
是的,在视图模型中引用视图不是最佳实践。为什么?因为当您对视图模型进行单元测试时,需要您实例化视图,对于小视图并不难做到这一点,但是对于具有复杂依赖树的复杂视图?那不会很好。
对我来说,与视图进行通信的最简单方法是传递IInputElement
视图模型构造函数。的好处IInputElement
是路由事件主干,它具有路由事件所需的方法RaiseEvent
。AddHandler
因此,您可以自由地将事件冒泡/隧道/直接到应用程序上的任何视图或视图模型,而无需任何额外的库。
这是我在视图模型上的简化代码,但请记住这种技术仅适用于视图优先方法
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));
}
}
简单地在您的视图上
DataContext = new MyViewModel(this)
//notice "this" on the constructor
以及您的应用程序的根视图(窗口)
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 这样的模拟库
如需进一步阅读,您可以了解有关如何使用路由事件的更多信息
您可以在 ViewModel 中定义一个操作
public Action CloseAction { get; set; }
然后,在您的窗口中(例如在 DataContextChanged 中)您可以设置此操作:
((IClosable)viewModel.Content).CloseAction = () => System.Windows.Application.Current.Dispatcher.Invoke(Close());
好吧,所有这些都是更大的依赖注入模式的一部分,但基本原理在这里......接下来,您只需要从 VM 调用操作。
此任务有一个有用的行为,它不会破坏 MVVM,这是一种在 Expression Blend 3 中引入的行为,允许 View 挂钩到完全在 ViewModel 中定义的命令。
此行为演示了一种允许 ViewModel 管理 Model-View-ViewModel 应用程序中 View 的关闭事件的简单技术。
这允许您在视图 (UserControl) 中连接一个行为,该行为将提供对控件窗口的控制,允许 ViewModel 控制是否可以通过标准 ICommands 关闭窗口。
使用行为允许 ViewModel 管理 MV-VM 中的视图生命周期
http://gallery.expression.microsoft.com/WindowCloseBehavior/
如果确实需要,让 ViewModel 执行此操作。
例如,模型说,不再有有效数据
将该信息传递给 ViewModel
ViewModel 认识到,它不能再显示任何东西
然后关闭窗口。
空视图是表示没有更多数据的正常方式