在 WPF 中使用 MVVM,我应该从 View 代码隐藏还是 ViewModel 启动子窗口?
我对此感到困惑有一段时间了。我正在使用 MVVM 模式编写一个相当大的 RibbonWindow
WPF 应用程序。屏幕顶部有一个 RibbonBar 菜单,其余部分显示各种视图。某些视图包含其他视图,其中一些具有启动子窗口的按钮。
到目前为止,我一直在查看文件后面的代码来执行此操作,但我知道使用 MVVM 时这些文件应该为空。我可以将子窗口启动代码移至 ViewModel,但随后我需要对主 RibbonWindow
的引用(设置为子窗口所有者),这似乎不对。
任何有关如何使用 MVVM 通常实现此目的的建议或提示将不胜感激。
I've been puzzled by this for a while. I am writing quite a large RibbonWindow
WPF application using the MVVM pattern. The screen has a RibbonBar
menu along the top and the rest of it displays the various Views. Some Views contain other Views and some of these have buttons that launch child Windows.
So far, I have been doing this from the View code behind file, but I'm aware that these files are supposed to be empty when using MVVM. I could move the child window launch code to the ViewModel, but then I would need a reference to the main RibbonWindow
(to set as the child window owner) and that doesn't seem right.
Any advice or tips on how this is normally achieved using MVVM would be greatly appreciated.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
我通常通过创建某种 WindowViewLoaderService 来处理这个问题。当您的程序初始化时,您可以使用如下代码注册您的 Window 和 ViewModel:
然后,当您可以从您的 ViewModel 调用此服务时,您所需要引用的就是您的其他 ViewModel。例如,如果您在 MainWindowViewModel 中,则可能有如下代码:
WindowViewLoaderService 然后会查找与您传递给它的指定 ViewModel 关联的 View。它将创建该视图,将其 DataContext 设置为您传入的 ViewModel,然后显示该视图。
这样你的 ViewModel 永远不会知道任何 View。
您可以轻松地推出自己的其中一项服务。它所需要做的就是保留一个字典,其中键是您的 ViewModelType,值是您的 ViewType。 Register 方法添加到字典中,ShowWindow 方法根据传入的 ViewModel 查找正确的视图,创建视图,设置 DataContext,然后对其调用 Show。
大多数 MVVM 框架都为您提供开箱即用的类似功能。例如,Caliburn 有一个巧妙的工具,它仅使用命名约定,在此框架中称为 ViewLocator。以下是总结的链接: http://devlicio.us/blogs/rob_eisenberg/archive/2010/07/04/mvvm-study-segue-introducing-caliburn-micro.aspx
另一方面,Cinch 将其称为 WPFUIVisualizerService你可以在这里看到实际效果:
http://www.codeproject.com/KB/WPF/CinchIII.aspx
这些应该可以帮助您顺利进行。
I usually handle this by creating some sort of WindowViewLoaderService. When your program initializes you register your Window's and your ViewModels with code something like this:
Then when you can for example call into this service from your ViewModel and all you have to reference is your other ViewModel. For example if you are in your MainWindowViewModel you might have code like this:
The WindowViewLoaderService would then look up what View is associated with the specified ViewModel you passed it. It will create that View, Set its DataContext to the ViewModel you passed in, and then display the View.
This way your ViewModels never know about any Views.
You can roll your own one of these services pretty easily. All it needs to do is keep a Dictionary with the key being your ViewModelType and the value being your ViewType. The Register method adds to your dictionary and the ShowWindow method looks up the correct view based on the ViewModel passed in, creates the view, sets the DataContext, and then calls Show on it.
Most MVVM Frameworks provide something like this for you out of the box. For example Caliburn has a slick one that just uses naming convention its called ViewLocator in this Framework. Here is a link that summarizes: http://devlicio.us/blogs/rob_eisenberg/archive/2010/07/04/mvvm-study-segue-introducing-caliburn-micro.aspx
Cinch on the other hand calls it a WPFUIVisualizerService which you can see in action here:
http://www.codeproject.com/KB/WPF/CinchIII.aspx
These should help get you rolling.
好吧,首先要说的是,“代码隐藏中根本没有代码”实际上是一个“神话”。如果您想要务实,并且您发现拥有一些代码(越少越好)将使您的生活更轻松并解决您的问题,那么您应该这样做。
然而,在这种情况下,实际上有一些松散耦合的方法可以做到这一点。您可以拥有一项为您进行交互的服务。您从 ViewModel 发起与用户的交互,服务负责处理该交互(例如通过显示 ChildWindow),并返回用户的响应。可以轻松模拟该服务以进行测试。并且可以单独测试。
也就是说,如果你想自己做事。如果您想要一个框架为您完成繁重的工作,您可以查看 Prism 提供的
InteractionRequest
功能。这是讨论高级 MVVM 方案 其中包括关于 用户交互模式。我就是这样做的,而且非常简单、优雅和直接。希望这有帮助:)
Well, one remark to start with is that, "Having no code AT ALL in the code-behind" is actually a "myth". If you want to be pragmatic, and you see that having some code (as little as possible would be better), will make your life easier and solve your problem, then you should go with that.
However, in this situation, there are actually some loosely coupled ways to do this. You could have a service that does the interaction for you. You initiate the interaction with the user from the ViewModel, the service takes care of that (by showing a ChildWindow for example), and gives you back the user's reponse. That service can be mocked for testing easily. And it can be tested seperately.
That is, if you want to do things yourself. If you want a framework to do the heavy lifting for you, you can check out the
InteractionRequest
functionaity offered by Prism. Here's the MSDN article that talks about adanced MVVM scenarios which includes a section on User Interaction Patterns. That's the way I do it, and it's pretty simple, elegant and straightforward.Hope this helps :)
为了使马特的答案更进一步,您可以让所有视图成为用户控件。然后创建一个 ViewContainer,它是一个包含数据模板的窗口(如您所描述的)。
然后,您只需将想要打开的视图模型发送到窗口服务,该服务设置 DataContext。然后,服务将打开窗口,内容控件将为视图模型解析正确的视图。
这意味着所有注册都是在 XAML 中完成的,窗口服务只知道如何做到这一点...打开和关闭窗口。
To take Matt's answer one step further, you can have all your view's be a user control. Then create a ViewContainer, which is a window with your data templates (as you described).
Then you just ship the viewmodel you wish to open over to the window service, which sets the DataContext. The service would then open the window and the contentcontrol will resolve the correct view for the viewmodel.
This means all the registration is done in the XAML and the window service just knows how to do just that...open and close windows.
这是一篇旧文章,但也许这会对某人有所帮助:我使用 MVVM,并引发从 ViewModel 打开子窗口返回到 View 的事件。背后唯一的代码是处理事件、打开窗口、设置子窗口的所有者,仅此而已。在视图模型中,如果事件处理程序为空,则视图不会订阅它并且不会触发。 VM 不知道该视图。代码也非常简单,只需要几行。
This is an old post, but maybe this'll help someone along the way: I use MVVM, and raise events for opening child windows from the ViewModel back to the View. The only code behind is handling the event, opening the window, setting owner of the child window and that's pretty much it. In the viewmodel, if the eventhandler is null, then it's not subscribed to by the view and doesn't fire. The VM does not know about the view. The code is pretty simple also and only takes a few lines.
在这种情况下,View 应该处理子窗口的打开。
但是,ViewModel 可能会驱动窗口的创建,但会调用 View 来创建新的窗口。
这将节省 MVVM 模式的逻辑:ViewModel 拥有“大脑”,但不参与特定窗口的创建。
In this situation View should handle the opening of the child windows.
However, ViewModel might drive the creation of the windows, but calling into View to create a new Windows.
This will save the logic of MVVM pattern: ViewModel has the "brains" but is not involved in a particular window creation.
ViewModel 仅用于呈现系统状态和 UI 逻辑。一个视图模型可以被多个视图引用。它不知道 UI 特定代码,如父/子关系、位置、布局、大小等。因此,最好使用 ViewModel 的状态更改事件或命令事件和事件参数在视图的代码隐藏中弹出子窗口。这样就可以在UI层指定哪一个是父视图。
ViewModel only is used to present system state and UI logic. One viewmodel may be referenced by multiple views. It have no knowledge of UI specific code like parent/child relationship, position, layout, size etc. So it is better to pop child window in view's code-behind with ViewModel's state changed event or command event and event arguments. In this way you can specify which one is the parent view in the UI layer.