WPF 中干净优雅的视图模型

And*_*Gis 5 .net c# wpf mvvm

一旦我发现了 MVVM,我就非常喜欢它。绑定、将视图与逻辑分离、可测试性等的整个概念非常令人鼓舞。对于背后杂乱无章的代码,这是一个不错的选择。后来我了解到有一些可以绑定的命令,我一开始也很喜欢。

使用 MVVM 编写了几个控件后,我发现我的视图模型开始看起来或多或少像背后的代码。充满了几乎与之前在事件处理程序背后的代码中所做的完全相同的命令。

让我给你举几个例子。

有一个带有“详细信息”按钮的控件,可打开另一个窗口。

[方法 1] 你可以做的第一件事(也是最糟糕的)就是在你的命令中调用这样的东西:

new DetailsWindow().ShowDialog();
Run Code Online (Sandbox Code Playgroud)

这使得视图模型强烈引用表示层 - 丑陋。

[方法 2] 让我们使用弱引用来解决这个问题,并创建类似 IDialogService 的东西。我们可以注入一个简单的实现来创建和打开窗口。现在我们摆脱了对表示层的强引用,命令看起来像这样:

_dialogService.ShowDetailsWindow();
Run Code Online (Sandbox Code Playgroud)

我仍然不喜欢这种方法。对我来说,感觉视图模型不是应该决定是否显示窗口的东西。它应该服务和处理数据。

[方法 3] 将视图模型与表示层完全分离的优雅方法是注入命令本身。比视图模型不会知道表示层。它只会执行注入的动作——不管它是什么。

问题 1:

哪种方法最好?我猜数字3是赢家。

问题2:

这甚至应该成为视图模型的一部分吗?我认为不应该,因为它似乎是表示层的关注点。也许最好将它放在代码后面的简单事件处理程序中?

第二个例子更复杂。在同一个控件中,我们有一个“删除”按钮。它应该打开一个对话框,要求用户确认,如果他说“是”,它应该删除一些东西。在这种情况下,将它放在视图模型中更有意义,因为它确实会影响数据。

问题 3

这个案子对我来说是最棘手的。我不能用我最喜欢的方法数3,因为我要显示一个对话框,这是一个表现层的工作,而且我必须执行根据对话的结果有些逻辑-这在另一方面是视图模型的工作。这里最好的方法是什么?

请记住,我真的不想使用方法12。我希望视图模型是干净的并且不知道与表示层相关的任何事情 - 甚至不知道弱引用。

我想到的一件事是将视图模型层分成两层。就像是:

视图 --> 表示视图模型 --> 逻辑视图模型

  • 演示视图模型
    • 用作控件的上下文
    • 包含逻辑视图模型作为直接绑定的公共属性
    • 使用方法2 - 现在它是可以接受的,因为整个班级都旨在执行与演示相关的操作
  • 逻辑视图模型
    • 它是“免费演示”
    • 参考专业逻辑服务
    • 一些命令可以直接绑定到视图
    • 某些命令可以由拥有它的演示视图模型执行

也许这是正确的方法?

[编辑]

回应有关使用框架的评论中的建议:

使用框架肯定会更容易处理窗口,但这不是解决问题的方法。我根本不希望“逻辑视图模型”处理窗口,甚至不需要框架的帮助。参考我最后建议的方法,我仍然会将它放在“演示视图模型”中

Dea*_*uga 0

您的 ViewModel 应该通过触发一个简单的事件来通知所有订阅者该命令已被执行。视图应该订阅该事件并通过显示新窗口来处理该事件...

视图模型:

public event EventHandler<NotificationEventArgs<string>> DisplayDetailsNotice;

private DelegateCommand displayDetailsCommand;
public DelegateCommand DisplayDetailsCommand
{
    get { return displayDetailsCommand ?? (displayDetailsCommand = new DelegateCommand(DisplayDetails)); }
    }

public void DisplayDetailsWindow()
{
    //
    Notify(DisplayDetailsNotice, new NotificationEventArgs<string("DisplayDetails"));
}
Run Code Online (Sandbox Code Playgroud)

View(注意VM已经是View的DataContext):

private readonly MyViewModel mvm;

//Get reference to VM and subscribe to VM event(s) in Constructor

mvm = DataContext as MyViewModel;
if (mvm == null) return;
mvm.DisplayDetailsNotice += DisplayDetails;

private void DisplayDetails(object sender, NotificationEventArgs<string> e)
{
    DetailsWindow = new DetailsWindow { Owner = this };
    DetailsWindow.ShowDialog();
}
Run Code Online (Sandbox Code Playgroud)

这样,当执行命令时,VM 引发事件,并且订阅 VM 事件的视图通过使用该ShowDialog()方法显示详细信息窗口来处理该事件。由于 VM 所做的只是发布一个事件,因此它与视图保持不可知并分离,从而保持 MVVM 模式完好无损。由于 VM 已经是视图的 DataContext,并且视图绑定到 VM 的属性,因为它获取对 VM 的引用也不会破坏 MVVM 模式...

请注意,此语法对于 Simple MVVM Toolkit MVVM 框架有效。正如评论中所建议的,您应该开始使用 MVVM 框架(我的建议是 Simple MVVM Toolkit 或 MVVM Light,Caliburn 无缘无故太复杂,在我看来也没有任何收获),以避免自己处理管道......