Navigation with MVVM

NOTE: I have re-written this article here to provide a better example, and some code samples.


When I first started out with MVVM, I was lost about how you should navigate between pages. I’m a firm believer in using ViewModels to do everything (unless it’s View-specific code), and that the UI is simply a user-friendly interface for your ViewModels. I did not want to create a button on a page that has any kind of code-behind to switch pages, and I didn’t like the idea of my navigation being spread out throughout all the ViewModels.

I finally came to realize the solution was simple: I needed a ViewModel for the Application itself, which contained the application state, such as the CurrentPage.

The ViewModel

Usually I name the ViewModel ShellViewModel or AppViewModel, but you can call it whatever you want. It is the startup page or window in the application, and is usually the only page/window object in my project.

It looks something like this:

    public class ShellViewModel : INotifyPropertyChanged
    {
        private ICommand _changeViewModelCommand;

        private object _currentViewModel;
        private List<object> _viewModels = new List<object>();

        public ShellViewModel()
        {
            ViewModels.Add(new HomeViewModel());
            CurrentViewModel = ViewModels[0];
        }

        private void ChangeViewModel(object viewModel)
        {
            if (!ViewModels.Contains(viewModel))
                ViewModels.Add(viewModel);

            CurrentViewModel = ViewModels.FirstOrDefault(vm => vm == viewModel);
        }
    }

I have omitted the public property implementation and the INotifyPropertyChanged interface methods for sake of readability. The public properties are your standard get/set properties that raise a PropertyChange notification, and the public ChangeViewModelCommand points to the ChangeViewModel method.

The View

I also need a ShellView for my ShellViewModel, so this is what it usually looks like:

<Grid Margin="20">
        <Grid.RowDefinitions>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="Auto"/>
            <RowDefinition Height="*"/>
        </Grid.RowDefinitions>

        <!-- Header -->
        <TextBlock Text="Application Name" FontWeight="Bold" FontSize="24" />

        <Line Grid.Row="1" Stroke="Black" Margin="0,5" StrokeThickness="1" Stretch="Fill" X2="1" />        <!-- Content -->
        <ContentControl Grid.Row="2" Content="{Binding CurrentViewModel}"/>
</Grid>

It contains a standard frame for the application, in this case the application name with a horizontal line under it, followed by the CurrentPage in a ContentControl. When a page is displayed in the ContentControl, WPF will lookup the appropriate DataTemplate to use to display that ViewModel (page).

Changing Pages

The ChangePage command can either be called directly through the XAML using something like this:

<Button Command="{Binding RelativeSource={RelativeSource
            AncestorType={x:Type Window}},
            Path=DataContext.ChangeViewModelCommand}
        CommandParameter="{Binding EditCustomerViewModel}"
        />

or you can use an Event system such as PRISM’s EventAggregator or MVVM Light’s Messenger, and the ViewModels would raise a ChangePage event. I prefer to use an Event system personally, but use whatever works for you.

For more on switching between Pages/Views, see this article

11 Responses to Navigation with MVVM

  1. Julio says:

    Very nice implementation for navigation. I started working with WPF recently but also with Windows Forms I had the same question.
    At the end I resolved using Caliburn Micro, a very nice framework. Wonder if you’ve used it.

    Anyways, I really admire the way you came out with your own solution without extra tools.

    Keep up the good work!

  2. Luc Bos says:

    I don’t agree with your statement ‘I’m a firm believer in using ViewModels to do everything’. Then what differentiates a ViewModel from a Codebehind when you design by that belief?

    Also this can’t be used with Silverlight 4 and below unless you write your own look-up mechanism. This is useful however in WPF.

    • Rachel says:

      I use the Code-Behind to do anything UI-related (set focus, run animations, etc), and the ViewModels for everything else. The whole point of MVVM is to decouple the Interface from the Business Logic, and you can’t do that if you perform business logic in the code-behind of your interface.

      I realize that some people prefer a View-First approach, which is fine, but personally I prefer ViewModel-First since it separates the UI from the app logic.

      Also, I have used this sort of solution for navigation in Silverlight before, and it works great. I used PRISM’s EventAggregator to fire ChangePage events to the ShellViewModel from within other ViewModels.

  3. Steve says:

    Hi Rachel,

    thanks for this post. I’ve just started using wpf and mvvm so it’s still unclear how to load a new page.

    You’re passing a ViewModel to the changePage command. Must this ViewModel have a reference to the view or how do i now which View i have to display.

    Also: I can’t load a page or window inside of a ContentControl so I assume you use DataTemplates?

    It would be nice if you can give me a more detailed example.

    Regards, Steve

    • Steve says:

      Found the solution in your other post “Switching between Views/UserControls using MVVM”

      I wasn’t aware of the fact, that i can use DataType on a DataTemplate.

    • Rachel says:

      Hi Steve!

      I prefer to let my ViewModels, not my Views, be the application, so as a result I usually only have a single Page/Window object in my application: the startup ShellView. Everything else is either a UserControl or DataTemplate.

      I should probably reword my code sample to use View instead of Page, since Page implies that the objects being displayed are Page objects, not ViewModels/Views.

      Glad you found my other post and got it worked out though! Thanks for the feedback :)

  4. Mark Wiskochil says:

    Rachel: I love your blog, it has really helped me with MVVM. I have my first two projects working. I am asking if you have a project for this view switching that I could look at. The code sample here doesn’t have the methods for ViewModels, HomeViewModel, and CurrentViewModel. Thanks in advance – Mark Wiskochil

  5. Mark Wiskochil says:

    Sorry I left the previous comment, I was tired when I wrote it. I got everything figured out except I get a binding error on the button command. The command parameter binding fails to find the viewmodel. on the datacontext. This makes sense, but I don’t know how to fix that. I am using a hack to get around that.

    • Rachel says:

      Hi Mark, I’ve been meaning to re-write this entry to be a little clearer and include some code samples but haven’t had the time yet. I’ll see about getting that done in the next weekend or two.

      In response to your question, the button’s command binding is saying that the Button should look up the Visual Tree until it finds a tag, then look in that Window.DataContext and bind to the ChangeViewModelCommand property.

      The Window.DataContext should be a ShellViewModel. I didn’t actually write the public definition for this command since it is fairly generic, however it should look like this:

      public ICommand ChangeViewModelCommand
      {
          get
          {
              if (_changeViewModelCommand == null)
              {
                  _changeViewModelCommand = 
                      new RelayCommand<object>(ChangeViewModel);
              }
      
              return _changeViewModelCommand;
          }
      }
      

      The Button’s CommandParameter is referencing a property on it’s DataContext called EditCustomerViewModel. I realize now this is a bad example since I never created an EditCustomerViewModel anywhere.

      As a simple alternative for testing, you can add your Button’s CommandParameter in the code behind.

      myButton.CommandParameter = new SomeViewModel();
      

      The end result is that when the button is clicked, the code that gets executed is the same as calling the line below, except none of the layers need to reference the others directly.

      ShellViewModel.ChangeViewModel(myButton.CommandParameter);
      

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 89 other followers

%d bloggers like this: