当谈到弹出窗口时,我有一个关于 VM 职责的问题。当应用程序弹出消息框或某种对话框(使用 MVVM)时,我们有两个选项:
- 将 UI (ShowDialog()) 代码放在 VM 中,这似乎很糟糕
- 让 VM 发送某种 UI 可以订阅的事件并在后面的代码中显示一个对话框(但我们正在努力实现后面的零代码:))
各位大佬是怎么处理这个案子的?
当谈到弹出窗口时,我有一个关于 VM 职责的问题。当应用程序弹出消息框或某种对话框(使用 MVVM)时,我们有两个选项:
各位大佬是怎么处理这个案子的?
看看玛瑙。这是一个基于使用服务和服务定位器或依赖注入模式的 MV-VM 库(完全公开:我是作者)。有 MessageBox 和常用对话框的服务,添加自己的服务也很容易。
不要将 UI 代码放在 VM 中,这只会导致很多麻烦。
当您想要弹出窗口或对话时,通常有两种情况。您这样做是因为商业案例,例如双击列表的详细视图,或者它完全基于 UI,例如弹出选项窗口。在第一种情况下,最好在 VM 中使用事件,在后一种情况下,我只使用事件处理程序。一个好的经验法则是,如果您不需要任何(重要的)VM 变量来完成操作,那么您应该只使用事件处理程序。
最重要的是,用你的头脑并相信你的判断,你很快就会学会使用哪个。
其他人未提及的其他几个选项:
中继命令
VM 执行一个我喜欢称之为“中继命令”的命令。这是由其他人处理的命令,VM 不在乎谁。命令执行只会引发Executed
事件。您的视图将订阅此事件并以新Window
的形式显示内容(内容将作为命令参数传递)。
请注意,中继命令不是路由命令。它不在其执行逻辑中搜索处理程序。它只是引发了一个事件。
一项服务
如果有很多情况需要在窗口中显示某些内容,请编写一个 UI 服务来处理它。虚拟机然后依赖这个服务(可以很容易地模拟)在窗口中显示内容。
我想说最好的方法是Popup
在 XAML 中定义,然后使用DataTrigger
绑定到您的某些条件ViewModel
来隐藏或显示它。然后,如果您关心处理来自 的返回值,请Popup
在EventTrigger
操作Popup
属性ViewModel
中反映该更改。
有很多关于这类领域的讨论,我认为这是因为人们习惯于在 WinForms 世界中编程。除了获取初始数据或设置之外,我还没有找到在视图中需要任何代码的解决方案DataContexts
即使您打算使用消息框,也为您的弹出对话框创建一个界面。
在视图层次结构的视图底部有一个方法让演示者注册实现弹出窗口的类或表单。
让您的视图调用已注册的弹出窗口。
有一个 ViewModel 用于弹出窗口和 View 作为用户控件。根据弹出窗口的复杂性,它可以是通用虚拟机,也可以是业务案例的具体虚拟机。当试图从父虚拟机显示它时,为弹出窗口的虚拟机创建一个“主机”类(从窗口或弹出窗口继承),显示它并将虚拟机分配给它。主机应负责定位正确的视图(通过 DataTemplate)
在这种情况下,您的弹出窗口的虚拟机仍然是可测试的,并且与父虚拟机的 WPF 耦合的最低级别是可以接受的,IMO。
我同意 Carlos 的观点,即在视图中使用事件订阅不是一个好的选择。据我了解 MVVM,它可以消除视图中的任何代码,我真的很喜欢它 :)。MVVM 的优点之一是能够对其进行单元测试。但是我仍然相信即使弹出窗口也可以测试视图模型。我们所需要的只是使用带有所需视图模型的初始化内容的窗口。我认为VM应该打开新对话框可能有两种可能的情况:显示选项/设置窗口或显示取决于业务逻辑的窗口。在第一种情况下,新窗口代码是命令的唯一代码(“设置”或“选项”按钮/菜单项的命令)。在第二种情况下,我们有一些打开窗口的决策逻辑。但是在任何情况下,我们都可以将打开新窗口的代码移动到单独的方法/类中,并且在测试我们的 VM 时只是为了模拟这个方法/类。更重要的是,这个单独的类可以是某种通用的 WindowsController,它将跟踪应用程序中的所有窗口。但是我们仍然可以说视图模型使用帮助程序 WindowsController 打开弹出窗口,并且视图对其他窗口一无所知。所有业务逻辑都封装在模型和视图模型中。
我使用视图处理的事件。我不是 100% 喜欢它,但它支持自动化测试。