以下问题基于此帖中的评论:MVVM Understanding Issues
我说这是代码隐藏,不违反视图和视图模型的关注点分离:
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;
}
}
评论是:
代码隐藏中的任何东西都不能进行单元测试,并且可以调用 创建一个对话框是逻辑,因此不应该在 图
我有两个问题:
我可以使用一些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中处理事件但是它带来了什么值?我仍然不能单元测试取消Windows关闭事件,但我已经介绍了发布和订阅,这也值得unittestig。它是另一层间接
也许我可以将事件路由到viewmodel:
public MainWindow()
{
InitializeComponent();
Closing += ViewModel.OnWindowClosing;
//or
Closing += (o, e) => ViewModel.OnWindowClosing(e);
}
但我没有看到与原始样本有太大差异。
恕我直言,view和viewmodel之间的连接无法在viewmodel测试中进行单元测试,所以我要么找到一种方法来测试视图,要么就是疯狂追逐。
答案 0 :(得分:4)
正如我所看到的,这里有两个问题。首先,您可以通过使用交互命名空间和命令来消除一些代码隐藏,以供参考:
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>
在显示对话框时,您需要考虑对话框是视图还是视图模型。当谈到确认窗户关闭时,我认为这是纯粹的观点。因此,您可以在Closing事件的代码隐藏中显示,而无需IMHO打破MVVM。
答案 1 :(得分:2)
关于第一个问题,我是发表评论的人,所以很明显我的回答是“是”:)
至于第二种,交互触发器是我自己通常实现的方式(尽管在情况决定时我也使用了附加行为):
<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>
Closing处理程序通过依赖注入框架调用对话框的创建,而Close处理程序使主视图模型自毁:
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();
}
这是一个非常简单的示例,但很明显,通过注入本地化管理器和对话框视图模型的模拟实例,然后直接从测试框架调用命令处理程序,可以很容易地测试此代码。
值得指出的是,在所有情况下打破纯MVVM并不一定是坏事。 Josh Smith在撰写他关于MVVM的原创文章时似乎非常支持没有代码隐藏,但到了“高级MVVM”时,他似乎采取了更为温和的立场,并指出“实用的开发人员采取中间道路并使用良好的判断确定哪些代码属于“。在将WPF集成到全栈体系结构的七八年中,我个人从未遇到过纯MVVM无法干净优雅地实现问题的情况,尽管在某些情况下会增加复杂性。你自己的里程会有所不同。