在WPF中使用MVVM,我应该从后面的View代码或ViewModel启动子窗口吗?

时间:2021-04-17 07:30:23

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.

我有一段时间对此感到困惑。我正在使用MVVM模式编写相当大的RibbonWindow WPF应用程序。屏幕顶部有一个RibbonBar菜单,其余部分显示各种视图。某些视图包含其他视图,其中一些视图具有启动子窗口的按钮。

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.

到目前为止,我一直在使用View代码隐藏文件执行此操作,但我知道在使用MVVM时这些文件应该是空的。我可以将子窗口启动代码移动到ViewModel,但是我需要一个对主RibbonWindow的引用(设置为子窗口所有者),这似乎不正确。

Any advice or tips on how this is normally achieved using MVVM would be greatly appreciated.

关于如何使用MVVM正常实现这一建议或提示将不胜感激。

6 个解决方案

#1


19  

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:

我通常通过创建某种WindowViewLoaderService来处理这个问题。当您的程序初始化时,您使用以下代码注册Window和ViewModel:

WindowViewLoaderService.Register(TypeOf(MainWindowView), TypeOf(MainWindowViewModel);
WindowViewLoaderService.Register(TypeOf(MyWindowView), TypeOf(MyWindowViewModel);

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:

然后,当您可以从ViewModel调用此服务时,您需要引用的只是您的其他ViewModel。例如,如果您在MainWindowViewModel中,则可能包含以下代码:

var myChildWindowVM = new MyWindowViewModel();
WindowViewLoaderService.ShowWindow(myChildWindowVM)

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.

然后,WindowViewLoaderService将查找与您传递的指定ViewModel关联的View。它将创建View,将其DataContext设置为您传入的ViewModel,然后显示View。

This way your ViewModels never know about any Views.

这样您的ViewModel就不会知道任何视图。

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.

您可以非常轻松地推出自己的其中一项服务。它需要做的就是保持一个Dictionary,键是你的ViewModelType,值是你的ViewType。 Register方法添加到您的字典中,ShowWindow方法根据传入的ViewModel查找正确的视图,创建视图,设置DataContext,然后在其上调用Show。

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

大多数MVVM框架为您提供开箱即用的功能。例如,Caliburn有一个光滑的,只使用命名约定,在本框架中称为ViewLocator。以下链接总结: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

另一方面,Cinch将其称为WPFUIVisualizerService,您可以在此处看到:http://www.codeproject.com/KB/WPF/CinchIII.aspx

These should help get you rolling.

这些应该有助于你滚动。

#2


6  

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.

但是,在这种情况下,实际上有一些松散耦合的方法可以做到这一点。您可以拥有一个为您进行交互的服务。您从ViewModel启动与用户的交互,服务负责(例如通过显示ChildWindow),并返回用户的响应。可以模拟该服务以便轻松进行测试。它可以单独测试。

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.

也就是说,如果你想自己做事。如果你想让一个框架为你做繁重的工作,你可以查看Prism提供的InteractionRequest功能。这是MSDN文章,讨论了高级MVVM场景,其中包括一个关于用户交互模式的部分。这就是我的方式,它非常简单,优雅和直接。

Hope this helps :)

希望这可以帮助 :)

#3


3  

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).

要进一步采用Matt的答案,您可以将所有视图都设置为用户控件。然后创建一个ViewContainer,它是一个包含数据模板的窗口(如您所述)。

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.

然后,您只需将要打开的viewmodel发送到窗口服务,该窗口服务设置DataContext。然后该服务将打开窗口,contentcontrol将解析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.

这意味着所有注册都在XAML中完成,窗口服务只知道如何操作...打开和关闭窗口。

#4


1  

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.

这是一篇旧帖子,但也许这会帮助一路上的人:我使用MVVM,并将用于打开子窗口的事件从ViewModel提升回View。后面唯一的代码是处理事件,打开窗口,设置子窗口的所有者,这就是它。在viewmodel中,如果eventhandler为null,则视图不会订阅它,也不会触发。 VM不知道该视图。代码也非常简单,只需几行。

#5


0  

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.

在这种情况下,View应该处理子窗口的打开。但是,ViewModel可能会驱动窗口的创建,但调用View来创建新的Windows。这将保存MVVM模式的逻辑:ViewModel具有“大脑”,但不参与特定的窗口创建。

#6


0  

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.

ViewModel仅用于呈现系统状态和UI逻辑。一个视图模型可以由多个视图引用。它不知道UI特定代码,如父/子关系,位置,布局,大小等。因此,最好使用ViewModel的状态更改事件或命令事件和事件参数在视图的代码隐藏中弹出子窗口。通过这种方式,您可以指定哪一个是UI图层中的父视图。

#1


19  

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:

我通常通过创建某种WindowViewLoaderService来处理这个问题。当您的程序初始化时,您使用以下代码注册Window和ViewModel:

WindowViewLoaderService.Register(TypeOf(MainWindowView), TypeOf(MainWindowViewModel);
WindowViewLoaderService.Register(TypeOf(MyWindowView), TypeOf(MyWindowViewModel);

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:

然后,当您可以从ViewModel调用此服务时,您需要引用的只是您的其他ViewModel。例如,如果您在MainWindowViewModel中,则可能包含以下代码:

var myChildWindowVM = new MyWindowViewModel();
WindowViewLoaderService.ShowWindow(myChildWindowVM)

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.

然后,WindowViewLoaderService将查找与您传递的指定ViewModel关联的View。它将创建View,将其DataContext设置为您传入的ViewModel,然后显示View。

This way your ViewModels never know about any Views.

这样您的ViewModel就不会知道任何视图。

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.

您可以非常轻松地推出自己的其中一项服务。它需要做的就是保持一个Dictionary,键是你的ViewModelType,值是你的ViewType。 Register方法添加到您的字典中,ShowWindow方法根据传入的ViewModel查找正确的视图,创建视图,设置DataContext,然后在其上调用Show。

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

大多数MVVM框架为您提供开箱即用的功能。例如,Caliburn有一个光滑的,只使用命名约定,在本框架中称为ViewLocator。以下链接总结: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

另一方面,Cinch将其称为WPFUIVisualizerService,您可以在此处看到:http://www.codeproject.com/KB/WPF/CinchIII.aspx

These should help get you rolling.

这些应该有助于你滚动。

#2


6  

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.

但是,在这种情况下,实际上有一些松散耦合的方法可以做到这一点。您可以拥有一个为您进行交互的服务。您从ViewModel启动与用户的交互,服务负责(例如通过显示ChildWindow),并返回用户的响应。可以模拟该服务以便轻松进行测试。它可以单独测试。

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.

也就是说,如果你想自己做事。如果你想让一个框架为你做繁重的工作,你可以查看Prism提供的InteractionRequest功能。这是MSDN文章,讨论了高级MVVM场景,其中包括一个关于用户交互模式的部分。这就是我的方式,它非常简单,优雅和直接。

Hope this helps :)

希望这可以帮助 :)

#3


3  

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).

要进一步采用Matt的答案,您可以将所有视图都设置为用户控件。然后创建一个ViewContainer,它是一个包含数据模板的窗口(如您所述)。

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.

然后,您只需将要打开的viewmodel发送到窗口服务,该窗口服务设置DataContext。然后该服务将打开窗口,contentcontrol将解析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.

这意味着所有注册都在XAML中完成,窗口服务只知道如何操作...打开和关闭窗口。

#4


1  

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.

这是一篇旧帖子,但也许这会帮助一路上的人:我使用MVVM,并将用于打开子窗口的事件从ViewModel提升回View。后面唯一的代码是处理事件,打开窗口,设置子窗口的所有者,这就是它。在viewmodel中,如果eventhandler为null,则视图不会订阅它,也不会触发。 VM不知道该视图。代码也非常简单,只需几行。

#5


0  

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.

在这种情况下,View应该处理子窗口的打开。但是,ViewModel可能会驱动窗口的创建,但调用View来创建新的Windows。这将保存MVVM模式的逻辑:ViewModel具有“大脑”,但不参与特定的窗口创建。

#6


0  

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.

ViewModel仅用于呈现系统状态和UI逻辑。一个视图模型可以由多个视图引用。它不知道UI特定代码,如父/子关系,位置,布局,大小等。因此,最好使用ViewModel的状态更改事件或命令事件和事件参数在视图的代码隐藏中弹出子窗口。通过这种方式,您可以指定哪一个是UI图层中的父视图。