MVVM 中的子窗口
我在理解 MVVM 方面遇到问题。我的应用程序依赖于对话框来完成某些事情。问题是,这些子窗口应该来自哪里?根据 MVVM,视图模型应该只包含业务逻辑,并且对 UI 的实际知识为零。但是,考虑到子窗口是 UI 元素,我还应该从其他什么地方调用它们呢?
这不会在元素之间创建紧密耦合吗?
I'm having a problem understanding something about MVVM. My application relies on dialogs for certain things. The question is, where should these childwindows originate from? According to MVVM, viewmodels should contain only businesslogic and have zero actual knowledge about UI. However, what other place should I call my childwindows from, considering they're UI elements?
Doesn't this create tight coupling between elements?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
既然您用 Prism 标记了这个问题,我将建议我过去使用 Prism 完成此问题的方式。将 IEventAggregator 注入到您的 ViewModel 中,然后当您想要弹出打开对话框时,发布“ShowDialogEvent”或类似的内容。然后,有另一个名为“DialogModule”或其他名称的模块,它在初始化时订阅该事件,并显示对话框。此外,如果您想将数据传递回原始 ViewModel,请让对话框的 ViewModel 发布一个“DialogCloseEvent”或类似的内容,其中包含您需要的数据的有效负载。然后,您可以在主 ViewModel 中订阅该事件。
Since you tagged the question with Prism, I'll suggest the way I've done it in the past using Prism. Have the IEventAggregator injected into your ViewModel, and then when you want to pop open the dialog, publish a "ShowDialogEvent" or something like that. Then, have another Module called "DialogModule" or whatever, which upon initialization subscribes to that event, and shows the dialog. Furthermore, if you want to pass data back to the original ViewModel, have the ViewModel of the dialog publish a "DialogCloseEvent" or something like that with a payload of the data you need. You can then subscribe to that event back in your main ViewModel.
请参阅使用 MVVM 处理 WPF 中的对话框
See Handling Dialogs in WPF with MVVM
过去,我通过使用 Unity 解析具有
Show()
方法和已完成事件的自定义界面来实现此目的。然后在 ViewModel 中,我将调用IScreen screen = container.Resolve(Resources.EditorWindowKey);
,然后调用screen.Show();
。这样做的一大优点是,当我测试虚拟机时,我只需更改 Unity 配置即可删除视图。
In the past, I have accomplished this by using Unity to resolve a custom interface that has a
Show()
method and a completed event. Then in the ViewModel I would callIScreen screen = container.Resolve<IScreen>(Resources.EditorWindowKey);
and then just callscreen.Show();
.The big advantage of this is that I can then just simply change my Unity configuration to remove the view when I'm testing my VM's.
我用来执行此操作的主要途径是在视图层内创建一个命令。该命令对象接受一个参数,该参数是您要显示的 ViewModel 对象。然后,该命令找到适当的 ChildWindow,创建它并使用设置为内容的参数或您设置的方式显示它。这样,您只需将按钮的命令属性绑定到该命令,并将其命令参数绑定到您想要在弹出窗口中显示的对象,并且您的 ViewModel 对象永远不必关心它的显示方式。
提示用户输入(例如保存脏文件或其他内容)在此方案中不起作用。但对于操作一些数据然后继续操作的简单弹出窗口,这种方法非常有效。
The primary route I've been using to do this is to create a command inside your View layer. That command object accepts a parameter that is the ViewModel object that you want to display. The command then finds the appropriate ChildWindow, creates it and displays it with the parameter set as the content or however you will set it up. This way you can just bind a button's command property to that command, and its commandparameter to the object you want to show in the popup and your ViewModel objects never have to care how it's being displayed.
Prompting for user input (like saving a dirty file or something) doesn't work in this scheme. But for simple popups where you manipulate some data and then move on, this works very well.
WPF 应用程序框架 (WAF) 的 ViewModel 示例应用程序演示了如何显示模态对话框。
The ViewModel sample application of the WPF Application Framework (WAF) demonstrates how to show a Modal Dialog.
我建议在这种情况下使用控制器,比如用对话框 shell 备份的 DI'ed 对话框控制器。源视图模型(即打开对话框的请求的发起位置)将调用
dialogController.ShowDialog(<>,<>)
。为了在对话框和源视图之间传输数据,您可以使用 MessageBus。因此,本质上,当您调用 ShowDialog() 时,您会填充消息总线,并且当调用目标视图(对话框 shell 中托管的视图)的关闭命令时 - 在“选择”按钮中说 - 让目标视图添加/更新消息总线。这样源视图模型就可以使用更新的数据。
它有很多优点:
1) 你的源视图与 BlackBox 一样与对话框控制器一起工作。即它不知道对话框视图到底在做什么。
2) 视图托管在 Dialog Shell 中——因此您可以一次又一次地重用对话框
3) 源视图的单元测试仅限于测试当前视图模型的实际功能,而不是测试对话框视图\视图模型。
我遇到的一个注意事项是,在创建测试用例时,您可能需要编写可测试的对话框控制器,该控制器在批量运行测试用例时不会显示实际的对话框。因此,您需要编写一个 TestableDialogController,其中 ShowDialog 不执行任何操作(除了从 IDialogController 派生并提供 ShowDialog() 的空白实现之外。
以下是伪代码:
I would suggest to use a controller in this scenario, say DI'ed dialogController backed up with a dialog shell. The source viewmodel(ie from where the request to open a dialog is originating) will make a call to
dialogController.ShowDialog(<<ViewNameToHostInRegion>>,<<RegionName>>)
.In Order to transfer the data to and from the dialog and sourceview you can use MessageBus. So essentially when you invoke the ShowDialog() you populate the messagebus, and when the close command of target View(The view hosted in Dialog shell) invoked - say in "Select" button -- Let the target view add/update the messagebus. So that source view model can work with updated data.
It has got many advantages :
1) Your source view works with dialog controller as BlackBox. ie it doesnt aware of what exactly the Dialog view is doing.
2) The view is being hosted in Dialog Shell -- so you can reuse the dialog again and again
3) Unit testing of source view is limited to test the actual functionality of the current viewmodel, and not to test the dialog view\view model.
One heads-up which I came across with this one is, while creating the test cases you may need to write testable Dialog controller which do not show the actual dialog while running the testcases in bunch. So you will need to write a TestableDialogController in which ShowDialog does nothing (Apart from deriving from IDialogController and provide blank implementation of ShowDialog().
Following is the psudeo code :