但是,我写了几个使用MVVM的控件之后,发现我的视图模型开始看起来有点像代码后台。充满了几乎与事件处理程序中以前完成的相同的命令。
让我给你举几个例子。
有一个带有“详细信息”按钮的控件,可以打开另一个窗口。 [方法1] 您可以在命令中调用以下内容(第一种(也是最糟糕的)方法):
new DetailsWindow().ShowDialog();
这造成了视图模型对表示层的强引用,很丑陋。
[方法2]采取使用弱引用并创建类似IDialogService的东西来解决这个问题。我们可以注入一个简单的实现来创建和打开窗口。现在我们摆脱了对表示层的强引用,命令可以像这样:
_dialogService.ShowDetailsWindow();
我仍然不喜欢这种方法。对我来说,视图模型不应该决定是否显示窗口,而应该服务于数据并处理数据。
[方法3] 完全将视图模型与表示层分离的优雅方式是注入命令本身。然后,视图模型将不知道表示层。它只会执行注入的操作 - 不管它是什么。
问题1:
哪种方法最好?我想数字3获胜了。
问题2:
这甚至应该是视图模型的一部分吗?我认为不应该,因为它似乎是表示层的关注点。也许把它放在代码后面的简单事件处理程序中会更好?
第二个例子更复杂。在同一个控件中,我们有一个“删除”按钮。它应该打开一个对话框,询问用户是否确认,如果他说“是”,则应该删除某些内容。在这种情况下,将其放在视图模型中更有意义,因为它确实影响数据。
问题3:
这种情况对我来说最棘手。我不能使用我最喜欢的第3种方法,因为我必须显示一个对话框,这是表示层的工作,但同时我还必须执行一些逻辑,这取决于对话框的结果 - 另一方面,这是视图模型的工作。这里最好的方法是什么?
请记住,我真的不想使用1和2的方法。我希望视图模型干净,并且不知道与表示层相关的任何内容 - 甚至不通过弱引用。
我想到的一件事是将视图模型层分成两个层。像这样:
视图 --> 表示视图模型 --> 逻辑视图模型
- 表示视图模型
- 用作控件的上下文
- 包含逻辑视图模型作为公共属性以进行直接绑定
- 使用第2种方法 - 现在它可以接受,因为整个类都旨在执行与表示相关的操作
- 逻辑视图模型
- 它是“无表示”的
- 引用专业逻辑服务
- 一些命令可以直接绑定到视图
- 一些命令可以由拥有它的表示视图模型执行
也许这是正确的方法?
[编辑]
针对评论中关于使用框架的建议做出回应:
使用框架可以更轻松地处理窗口,但这并不是解决问题的办法。我不希望“逻辑视图模型”处理任何窗口,即使借助框架的帮助也不行。参考我在最后提出的方法,我仍会将其放在“展示视图模型”中。
IWindowManager
接口,可以将指定的视图模型显示为常规或模态窗口。这意味着你只需要操作视图模型。 - Patryk Ćwiek_navigationService.Show(SomeDialogViewModel)
的方法,其中SomeDialogViewModel
包含对话框显示所需的所有数据,以及可能用于标识其应该作为对话框显示的标志。然后,由_navigationService
负责确定如何以及在哪里显示视图模型。 - Rachel