Using MVVM in WPF, should I launch child windows from View code behind, or ViewModel?
Asked Answered
L

6

19

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.

Lucky answered 6/5, 2011 at 20:56 Comment(3)
I disagree that View code behind files "should be empty". While there are a great many things that should NOT be in code behind it still has value. I use it for anything that is specific to the View layer, such as managing control Focus. I also use it to open subsequent Windows in WPF.Bushranger
+1 for Joel. MVVM implementation means that VIEW SPECIFIC code is the ONLY code that should sit in the code behind. Your viewmodel shouldn't have be handling things like VSM state changes (unless, perhaps, these changes are data driven, in which case I'll wrap it in a behavior or trigger exposed at the VM level)Fireweed
Thanks for your comments guys... they've really cleared some things up for me.Lucky
A
20

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

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.

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.

Amyl answered 6/5, 2011 at 21:12 Comment(4)
+1 Thanks for your detailed response. Although I find this approach interesting, my application is already quite developed. The Views are linked to the ViewModels using un-named DataTemplate Resources and ContentControls that have their Content properties bound to ViewModel properties in other ViewModels. It would be a lot of work to change this, but it's not out of the question... if only there was more time in a day!Lucky
Just so you know its not a one or the other approach usually. I almost always use DataTemplates to do this as well. However, as you pointed out - this doesn't work when you need to open a new window (or Dialog for that matter). When you think about it what you are doing with a DataTemplate is the same thing you do with a ViewLocator. You basically "register" a ViewModel to a View based on its DataType... anyways just wanted you to know I think you are doing it correctly the only issue is when DataTemplates can't be used.Amyl
Thanks for that additional comment - I think that I'll definitely look into this further.Lucky
And how/where do you define WindowViewLoaderService? Is it a singleton? Owned by the App class? Other?Hypersonic
E
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.

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

Exchangeable answered 6/5, 2011 at 21:36 Comment(1)
+1 Thanks for your comments and useful links. I actually investigated Prism a while ago and found that I didn't need what it offered and decided to write a custom lightweight framework instead... still trying to get it right though.Lucky
B
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).

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.

Braun answered 7/5, 2011 at 12:46 Comment(0)
W
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.

Wheatley answered 13/11, 2016 at 13:3 Comment(0)
A
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.

Acariasis answered 6/5, 2011 at 21:1 Comment(1)
But how does a View open a new Window?Lucky
S
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.

Sidon answered 25/5, 2017 at 7:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.