我对此感到困惑了一段时间。我正在使用MVVM模式编写相当大的RibbonWindow
WPF应用程序。屏幕顶部有一个RibbonBar
菜单,其余部分显示各种视图。某些视图包含其他视图,其中一些视图具有启动子视窗的按钮。
到目前为止,我一直在使用View代码隐藏文件执行此操作,但我知道在使用MVVM时这些文件应该是空的。我可以将子窗口启动代码移动到ViewModel,但是我需要引用主RibbonWindow
(设置为子窗口所有者),这似乎不对。
有关如何使用MVVM实现这一目标的任何建议或提示将不胜感激。
答案 0 :(得分:19)
我通常通过创建某种WindowViewLoaderService来处理这个问题。当您的程序初始化时,您可以使用以下代码注册Window和ViewModel:
WindowViewLoaderService.Register(TypeOf(MainWindowView), TypeOf(MainWindowViewModel));
WindowViewLoaderService.Register(TypeOf(MyWindowView), TypeOf(MyWindowViewModel));
然后,当您可以从ViewModel调用此服务时,您需要引用的只是另一个ViewModel。例如,如果您在MainWindowViewModel中,则可能包含以下代码:
var myChildWindowVM = new MyWindowViewModel();
WindowViewLoaderService.ShowWindow(myChildWindowVM);
然后,WindowViewLoaderService将查找与您传递的指定ViewModel关联的View。它将创建View,将其DataContext设置为您传入的ViewModel,然后显示View。
这样您的ViewModel就不会知道任何视图。
您可以轻松地推出自己的其中一项服务。它需要做的就是保持一个Dictionary,键是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
这些应该会帮助你滚动。
答案 1 :(得分:6)
嗯,开头的一句话是,“在代码隐藏中没有代码全部”实际上是一个“神话”。如果你想务实,并且你看到拥有一些代码(尽可能少会更好),会让你的生活变得更轻松并解决你的问题,那么你应该坚持下去。
然而,在这种情况下,实际上有一些松散耦合的方法来做到这一点。您可以拥有一个为您进行交互的服务。您从ViewModel启动与用户的交互,服务负责(例如通过显示ChildWindow),并返回用户的响应。可以轻易地模拟该服务以进行测试。它可以单独测试。
也就是说,如果你想自己做事。如果您希望框架为您做繁重的工作,您可以查看 Prism 提供的InteractionRequest
功能。以下是关于adanced MVVM scenarios的MSDN文章,其中包含User Interaction Patterns部分。这就是我的方式,它非常简单,优雅和直接。
希望这会有所帮助:)
答案 2 :(得分:3)
为了让Matt的答案更进一步,您可以让所有视图成为用户控件。然后创建一个ViewContainer,它是一个包含数据模板的窗口(如您所述)。
然后,您只需将要打开的viewmodel发送到窗口服务,该窗口服务设置DataContext。然后,该服务将打开窗口,contentcontrol将解析viewmodel的正确视图。
这意味着所有注册都在XAML中完成,窗口服务只知道如何...打开和关闭窗口。
答案 3 :(得分:1)
这是一篇旧帖子,但也许这会帮助某人:我使用MVVM,并将用于打开子窗口的事件从ViewModel提升回View。后面唯一的代码是处理事件,打开窗口,设置子窗口的所有者以及它几乎就是这样。在viewmodel中,如果eventhandler为null,那么它不会被视图订阅并且不会触发。 VM不知道该视图。代码也很简单,只需几行。
答案 4 :(得分:0)
在这种情况下,View应该处理子窗口的打开。 但是,ViewModel可能会驱动窗口的创建,但调用View来创建新的Windows。 这将保存MVVM模式的逻辑:ViewModel具有“大脑”,但不参与特定的窗口创建。
答案 5 :(得分:0)
ViewModel仅用于呈现系统状态和UI逻辑。一个视图模型可以由多个视图引用。它不了解UI特定代码,如父/子关系,位置,布局,大小等。因此,最好使用ViewModel的状态更改事件或命令事件和事件参数在视图的代码隐藏中弹出子窗口。通过这种方式,您可以指定UI层中的父视图。