wpf MVVM 中的 Window.Closing 事件处理程序
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/32253382/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Window.Closing event handler in MVVM
提问by Liero
Following question is based on comment in this post: MVVM Understanding Issues
以下问题基于这篇文章中的评论:MVVM 理解问题
I said that this is codebehind, that does not violate the view and viewmodel separation of concerns:
我说这是代码隐藏,它不违反关注点的视图和视图模型分离:
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;
}
}
The comments was:
评论是:
Anything in code-behind can't be unit tested, and invoking the creation of a dialog box is logic and therefore shouldn't be in the view
代码隐藏中的任何内容都无法进行单元测试,并且调用对话框的创建是逻辑的,因此不应出现在视图中
I have two questions:
我有两个问题:
- Does this break MVVM separation of conerns?
- How would you do it (better)?
- 这是否会破坏 MVVM 的 conerns 分离?
- 你会怎么做(更好)?
I could call the viewmodel method from xaml using some EventTriggers and CallMethod actions, but it does not make any difference.
我可以使用一些 EventTriggers 和 CallMethod 操作从 xaml 调用 viewmodel 方法,但这没有任何区别。
I could do use event aggregator:
我可以使用事件聚合器:
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;
}
}
and handle the event in viewmodel but does it bring any value? I still cannot unit test cancelling the windows closing event, but I have introduces publishing and subscribing that would be also worth unittestig. It's yet another layer of indirection
并在视图模型中处理事件但它带来任何价值吗?我仍然无法对取消窗口关闭事件进行单元测试,但我已经介绍了同样值得单元测试的发布和订阅。这是另一层间接
Maybe I could route the event to viewmodel:
也许我可以将事件路由到视图模型:
public MainWindow()
{
InitializeComponent();
Closing += ViewModel.OnWindowClosing;
//or
Closing += (o, e) => ViewModel.OnWindowClosing(e);
}
but I don't see much difference with the original sample.
但我看不出与原始样本有太大区别。
IMHO, the connection between view and viewmodel cannot be unittested in viewmodel tests, so I either find a way how to test views or it is wild goose chase.
恕我直言,视图和视图模型之间的连接不能在视图模型测试中进行单元测试,所以我要么找到一种如何测试视图的方法,要么就是野鹅追逐。
采纳答案by Mark Feldman
With respect to the first question I'm the person that made the comment so obviously my answer will be "Yes" :)
关于第一个问题,我是发表评论的人,所以显然我的回答是“是”:)
As for the second, interaction triggers are how I usually implement it myself, (although I've also used an attached behaviour when the circumstances dictated):
至于第二个,交互触发器是我通常自己实现它的方式,(尽管在情况需要时我也使用了附加行为):
<i:Interaction.Triggers>
<i:EventTrigger EventName="Closing">
<cmd:EventToCommand Command="{Binding ClosingCommand}" PassEventArgsToCommand="True" />
</i:EventTrigger>
<i:EventTrigger EventName="Closed">
<cmd:EventToCommand Command="{Binding ClosedCommand}" />
</i:EventTrigger>
</i:Interaction.Triggers>
The Closing handler invokes the creation of a dialog box via a dependency injection framework while the Close handler causes the main view model to self-destruct:
关闭处理程序通过依赖注入框架调用对话框的创建,而关闭处理程序导致主视图模型自毁:
public ICommand ClosingCommand { get { return new RelayCommand<CancelEventArgs>(OnClosing); } }
private void OnClosing(CancelEventArgs args)
{
#if !DEBUG
var locman = Injector.Get<LocalizationManager>();
var dlg = Injector.Get<CustomDialogViewModel>();
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();
}
This is a very simple example but it should be immediately obvious that this code can be easily tested by injecting mocked instances of both the localization manager and dialog box view model and then invoking the command handlers directly from the test framework.
这是一个非常简单的示例,但很明显,通过注入本地化管理器和对话框视图模型的模拟实例,然后直接从测试框架调用命令处理程序,可以轻松测试此代码。
It may be worth pointing out though that breaking pure MVVM is not necessarily a bad thing in all circumstances. Josh Smith appeared to be very much in support of no code-behind when wrote his original article on MVVM but by the time of "Advanced MVVM" he appears to have taken a softer stance, stating "Practical developers take the middle road and use good judgement to determine what code belongs where". In my seven or eight years of integrating WPF into full-stack architectures I personally have never encountered a case where pure MVVM couldn't implement a problem cleanly and elegantly, albeit admittedly at the expense of increased complexity in certain cases. Your own mileage will vary.
可能值得指出的是,打破纯 MVVM 并不一定在所有情况下都是坏事。Josh Smith 在撰写关于 MVVM 的原始文章时似乎非常支持无代码隐藏,但到“高级 MVVM”时,他似乎采取了更温和的立场,指出“实际开发人员采取中间道路并使用好的判断什么代码属于哪里”。在我将 WPF 集成到全栈架构中的七八年里,我个人从未遇到过纯 MVVM 无法干净优雅地实现问题的情况,尽管在某些情况下必须以增加复杂性为代价。您自己的里程会有所不同。
回答by user3690202
There are two issues here, as I see it. Firstly, you can eliminate some of that code-behind by using the interactivity namespace and commands, for reference look into:
在我看来,这里有两个问题。首先,您可以通过使用交互命名空间和命令来消除一些代码隐藏,以供参考:
xmlns:i="http://schemas.microsoft.com/expression/2010/interactivity"
<i:Interaction.Triggers>
<i:EventTrigger EventName="Closing">
ICommand goes here - bind to your VM
</i:EventTrigger>
</i:Interaction.Triggers>
when it comes to showing dialogs, you need to consider whether the dialog is view or view-model. When it comes to confirming the window closure, I think of that as purely view. So you can show that inside the code-behind of the Closing event, without IMHO breaking MVVM.
在显示对话框时,您需要考虑对话框是视图还是视图模型。在确认窗口关闭时,我认为那是纯粹的视图。因此,您可以在 Closing 事件的代码隐藏中显示这一点,恕我直言,不会破坏 MVVM。

