MVVM中的Window.Closing事件处理程序

以下问题基于此帖中的评论: MVVM了解问题

我说这是代码隐藏,它不违反视图和视图模型的关注点分离:

public partial class MainWindow : Window { public MainWindow() { InitializeComponent(); Closing += MainWindow_Closing; } void MainWindow_Closing(object sender, CancelEventArgs e) { var canExit = ViewModel.ShowConfirmExitDlg(); if (!canExit) e.Cancel = true; } } 

评论是:

代码隐藏中的任何东西都不能进行unit testing,并且调用对话框的创建是逻辑,因此不应该在视图中

我有两个问题:

  1. 这是否打破了MVVM与conerns的分离?
  2. 你会怎么做(更好)

我可以使用一些EventTriggers和CallMethod动作从xaml调用viewmodel方法,但它没有任何区别。

我可以使用事件聚合器:

 public partial class MainWindow : Window { private readonly IEventAggregator _eventAggregator; public MainWindow(IEventAggregator eventAggregator) { _eventAggregator = eventAggregator; InitializeComponent(); Closing += MainWindow_Closing; } void MainWindow_Closing(object sender, CancelEventArgs e) { var evt = new MainWindowClosingEvent(); _eventAggregator.Publish(evt); e.Cancel = evt.IsCancel; } } 

并在viewmodel中处理事件但是它带来了什么价值? 我仍然不能unit testing取消Windows关闭事件,但我已经介绍了发布和订阅,也值得unittestig。 这是另一层间接

也许我可以将事件路由到viewmodel:

 public MainWindow() { InitializeComponent(); Closing += ViewModel.OnWindowClosing; //or Closing += (o, e) => ViewModel.OnWindowClosing(e); } 

但我没有看到与原始样本有太大差异。

恕我直言,view和viewmodel之间的连接不能在viewmodel测试中进行unit testing,所以我要么找到一种方法来测试视图,要么就是疯狂追逐。

关于第一个问题,我是发表评论的人,所以很明显我的回答是“是”:)

至于第二个,交互触发器是我自己经常实现它的方式(尽管在情况决定时我也使用了附加行为):

         

Closing处理程序通过dependency injection框架调用对话框的创建,而Close处理程序使主视图模型自毁:

 public ICommand ClosingCommand { get { return new RelayCommand(OnClosing); } } private void OnClosing(CancelEventArgs args) { #if !DEBUG var locman = Injector.Get(); var dlg = Injector.Get(); dlg.Caption = locman[LogOffCaption]; dlg.Message = locman[LogOffPrompt]; dlg.OnCancel = (sender) => { args.Cancel = true; sender.Close(); }; dlg.Show(); #endif } public ICommand ClosedCommand { get { return new RelayCommand(OnClosed); } } private void OnClosed() { this.Dispose(); } 

这是一个非常简单的示例,但很明显,通过注入本地化管理器和对话框视图模型的模拟实例,然后直接从测试框架调用命令处理程序,可以很容易地测试此代码。

值得指出的是,在所有情况下打破纯MVVM并不一定是坏事。 Josh Smith在撰写他关于MVVM的原创文章时似乎非常支持没有代码隐藏,但到了“高级MVVM”时,他似乎采取了更为温和的立场,并指出“实用的开发人员采取中间道路并使用良好的判断确定哪些代码属于“。 在将WPF集成到全栈体系结构的七八年中,我个人从未遇到过纯MVVM无法干净优雅地实现问题的情况,尽管在某些情况下会增加复杂性。 你自己的里程会有所不同。

正如我所看到的,这里有两个问题。 首先,您可以通过使用交互命名空间和命令来消除一些代码隐藏,以供参考:

 xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"   ICommand goes here - bind to your VM   

在显示对话框时,您需要考虑对话框是视图还是视图模型。 当谈到确认窗户关闭时,我认为这是纯粹的观点。 因此,您可以在Closing事件的代码隐藏中显示,而无需IMHO打破MVVM。