39

Example #1: I have a view displayed in my MVVM application (let's use Silverlight for the purposes of the discussion) and I click on a button that should take me to a new page.

Example #2: That same view has another button that, when clicked, should open up a details view in a child window (dialog).

We know that there will be Command objects exposed by our ViewModel bound to the buttons with methods that respond to the user's click. But, what then? How do we complete the action? Even if we use a so-called NavigationService, what are we telling it?

To be more specific, in a traditional View-first model (like URL-based navigation schemes such as on the web or the SL built-in navigation framework) the Command objects would have to know what View to display next. That seems to cross the line when it comes to the separation of concerns promoted by the pattern.

On the other hand, if the button wasn't wired to a Command object and behaved like a hyperlink, the navigation rules could be defined in the markup. But do we want the Views to control application flow and isn't navigation just another type of business logic? (I can say yes in some cases and no in others.)

To me, the utopian implementation of the MVVM pattern (and I've heard others profess this) would be to have the ViewModel wired in such a way that the application can run headless (i.e. no Views). This provides the most surface area for code-based testing and makes the Views a true skin on the application. And my ViewModel shouldn't care if it displayed in the main window, a floating panel or a child window, should it?

According to this apprach, it is up to some other mechanism at runtime to 'bind' what View should be displayed for each ViewModel. But what if we want to share a View with multiple ViewModels or vice versa?

So given the need to manage the View-ViewModel relationship so we know what to display when along with the need to navigate between views, including displaying child windows / dialogs, how do we truly accomplish this in the MVVM pattern?

0

3 Answers 3

26

Navigation should always be handled in the ViewModel.

You're on the right track with thinking that the perfect implementation of the MVVM design pattern would mean you could run your application entirely without Views, and you can't do that if your Views control your Navigation.

I usually have an ApplicationViewModel, or ShellViewModel, which handles the overall state of my application. This includes the CurrentPage (which is a ViewModel) and the code for handling ChangePageEvents. (It's often also used for other application-wide objects such as the CurrentUser, or ErrorMessages too)

So if any ViewModel, anywhere, broadcasts a ChangePageEvent(new SomePageViewModel), the ShellViewModel will pickup that message and switch the CurrentPage to whatever page was specified in the message.

I actually wrote a blog post about Navigation with MVVM if you're interested

6
  • 2
    Interesting approach. Four comments: 1) Silverlight doesn't support the DataType property on DataTemplate so mapping the DataTemplate to the ViewModel isn't possible in SL. 2) This doesn't address the many-to-many possibilities between Views and ViewModels. 3) It doesn't handle child windows (or at least I don't see how). 4) It requires tight-coupling between your Application/Shell ViewModel and it's children (grandchildren, etc.) If I have 40 pages in my app, this ViewModel would be way to cumbersome to manage. Nov 16, 2011 at 19:46
  • That said, it is definitely something to consider. Nov 16, 2011 at 19:47
  • 1
    @SonOfPirate 1) Silverlight doesn't support implicit DataTemplate mapping (yet), however it does support a DataTemplateSelector, which is what I usually use for Silverlight apps. 2) I have used this in many-to-many situations before. For example, one ViewModel can have multiple Views, or one View can be associated with multiple ViewModels. For an example of the former, see rachel53461.wordpress.com/2011/05/28/…. For the later, you just need to specify that multiple ViewModels map to the same view in your DataTemplateSelector.
    – Rachel
    Nov 16, 2011 at 19:59
  • 1
    3) That's just a basic example. If you knew your application was going to be multiple windows, you would obviously alter the ShellViewModel to handle multiple CurrentPages 4) Once again, that was just a basic example. In reality, my PageViewModels are all based off some base class, so my ShellViewModel only works with the base class or interface, such as IPageViewModel. Really the biggest messy piece of mapping is the DataTemplateSelector which would have to map 40 Views to 40 ViewModels.
    – Rachel
    Nov 16, 2011 at 20:00
  • @SonOfPirate I hope that answered some of your questions. Feel free to look me up in Chat if you have others :)
    – Rachel
    Nov 16, 2011 at 20:01
7

For the sake of closure, I thought I would post the direction I finally chose to solve this problem.

The first decision was to leverage the Silverlight Page Navigation framework provided out-of-the-box. This decision was based on a several factors including knowledge that this type of navigation is being carried forward by Microsoft into Windows 8 Metro apps and is consistent with navigation in Phone 7 apps.

To make it work, I next looked at the work the ASP.NET MVC has done with convention-based navigation. The Frame control uses URIs to locate the 'page' to display. The similarity provided an opportunity to use a similar convention-based approach in the Silverlight app. The trick was making it all work together in an MVVM way.

The solution is the NavigationService. This service exposes several methods, such as NavigateTo and Back, that ViewModels can use to initiate a page change. When a new page is requested, the NavigationService sends a CurrentPageChangedMessage using the MVVMLight Messenger feature.

The view that contains the Frame control has its own ViewModel set as the DataContext that is listening for this message. When received, the name of the new view is put through a mapping function that applies our convention rules and set to the CurrentPage property. The Source property of the Frame control is bound to the CurrentPage property. As a result, setting the property updates the Source and triggers navigation.

Going back to the NavigationService. The NavigateTo method accepts the name of the target page. To make sure that the ViewModels have no UI concerns, the name used is the name of the ViewModel to display. I actually created an enumeration that has a field for each navigable ViewModel as a helper and to eliminate magic strings all over the app. The mapping function I mentioned above will strip the "ViewModel" suffix from the name, append "Page" to the name and set the full name to "Views{Name}Page.xaml".

So, for example, to navigate to the customer details view, I can call:

NavigationService.NavigateTo(ViewModels.CustomerDetails);

The value of CustomerDetails is "CustomerDetailsViewModel" which is mapped to "Views\CustomerDetailsPage.xaml".

The beauty of this approach is that the UI is completely decoupled from the ViewModels yet we have full navigation support. I can now reskin my application however and whenever I see fit without any code changes.

Hope the explanation helps.

3

Similar to what Rachel said, my mostly-MVVM application has a Presenter to handle switches between windows or pages. Rachel calls this an ApplicationViewModel, but in my experience, it generally has to do more than just be a binding target (such as receiving messages, create Windows, etc.) so it's technically more like a traditional Presenter or Controller.

In my application, my Presenter starts with a CurrentViewModel. The Presenter intercepts all communication between the View and the ViewModel. One of the things the ViewModel can do during an interaction is return a new ViewModel, which means a new page or a new Window should be displayed. The Presenter takes care of creating or overwriting the View for the new ViewModel and setting the DataContext.

The result of an action can also be that a ViewModel is "complete", in which case the Presenter detects this and closes the window, or pops this ViewModel off the VM stack and goes back to displaying the previous page.

2
  • How does the Presenter know what View to display? Nov 17, 2011 at 13:20
  • 1
    @SonOfPirate - This is normally done through WPF mechanisms. The Presenter just sticks the returned ViewModel in the visual tree, and WPF grabs the appropriate View, hooks up the DataContext, and puts that in the visual tree instead. You can do this by using DataTemplates that declare what ViewModel type they render, or you can create a custom data template selector. That's still within the realm of WPF features. Mar 19, 2013 at 11:28

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.