M-V-VM设计问题。从ViewModel调用View

18

我刚开始研究WPF应用程序的M-V-VM。到目前为止,除了这个特定问题之外,一切都很清晰...

我有一个ViewModel,我称之为Search。这个ViewModel绑定到一个数据网格并列出项目的结果。现在,我有一个命令需要打开另一个视图,即项目的详细信息。

在Search View中放置显示另一个视图的逻辑似乎不合适,它根本无法进行测试。

以下是我的ViewModel实现,它无法进行测试...

public class SearchViewModel
{
   public void SelectItem()
   {
     // I want to call the DetailsView from here
     // this seems wrong, and is untestable
     var detailsView = new DetailsView();
     detailsView.Show();
   }
}
在这种模式中,从ViewModel方法显示视图的逻辑放在哪里?
5个回答

19

正如Kiff所指出的:

视图不应该在UI层以下的任何地方实例化。视图模型(VMs)存在于该领域之下,因此这不是放置该逻辑的地方(正如您已经意识到的那样)。

几乎总会有一些UI级别的事件表明需要创建视图。在您的示例中,可能会是数据网格中行的双击事件。那将是新建并显示您的DetailsView窗口的位置。

您必须意识到M-V-VM与其他模式(如MVC或MVP)略有不同。视图模型(ViewModel)不直接了解UI。打开另一个视图是特定于视图的功能。视图模型(ViewModel)不应关心使用其数据的视图是什么或有多少个。我很可能永远不会通过命令打开视图。

alt text


13

视图应该永远不会在UI层以下的任何地方实例化。VM存在于该领域之下,因此这不是放置该逻辑的地方(正如您已经意识到的那样)。

几乎总会有一些UI级别事件指示需要创建视图。在您的示例中,可能是datagrid上的行(双击)单击事件。那将是新建并显示DetailsView窗口的地方。


感谢您的帮助,这似乎是一个可行的解决方案。虽然我无法对设置视图的逻辑进行单元测试(设置与注入的ViewModel交互的属性),但它使ViewModels可测试,而这是大部分逻辑所在的地方。 - Jab
1
如果一个简单的事件打开视图,那就很好。但是如果事件需要更多的操作、数据获取和验证呢?你会把这些东西也放到视图中吗?还是创建另一层间接性? - Sam
确实。和Sam这边的问题一样。如果在打开视图之前需要更多的数据或逻辑怎么办?(例如,如果属性x=1,则打开视图1;如果属性x=2,则打开其他视图) - Tom Deleu
@Sam @Tom:事件的参数将从UI传递所需的任何数据。然后,处理程序使用该数据进行决策和/或检索进一步的数据。我喜欢将我的事件处理程序放在控制器上,以使我的VM尽可能轻量化。请查看Prism 2.0中的事件聚合和命令模式,这是一个很好的例子。它实际上展示了几种不同的方法。 - Chris Staley

4

以下是关于这个问题的基本经验法则。

  • 如果您正在处理视图中的本地操作,可以从视图模型中发起操作。

  • 如果涉及跨视图的操作(例如显示搜索屏幕),则可以使用事件聚合器模式(一种事件服务),或注入应用程序控制器并调用其方法,然后它会显示搜索结果。


1

Catel 包括一种使用 IUIVisualizerService 的方法。 这个接口定义了一个 UI 控制器,可以用于从 ViewModel 中以模式或非模式形式显示对话框。 基本上,在父 VM 中,您创建应该留在新视图后面的 viewmodel,而服务会查找关联的 viewmodel(基于某些约定或注册),然后显示它。


0
我们在这个模式上使用了一种变体,这里我们有代表VM的控制器,因此视图的数据上下文是VM,我们的DTO是VM/Controller的属性。我们仍然称其为控制器,因为我们将其用作控制点,从而处理来自视图的某些命令。这就是(我认为)我们会实现像你这样的命令的地方。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接