Navigation with MVVM

December 18, 2011

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.

Here is an example that builds on the Simple MVVM Example.

The ViewModel

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

It usually contains

    List<ViewModelBase> PageViewModels
    ViewModelBase CurrentPage
    ICommand ChangePageCommand

Here is an example ApplicationViewModel that I would use to go with the Simple MVVM Example.


    public class ApplicationViewModel : ObservableObject
    {
        #region Fields

        private ICommand _changePageCommand;

        private IPageViewModel _currentPageViewModel;
        private List<IPageViewModel> _pageViewModels;

        #endregion

        public ApplicationViewModel()
        {
            // Add available pages
            PageViewModels.Add(new HomeViewModel());
            PageViewModels.Add(new ProductsViewModel());

            // Set starting page
            CurrentPageViewModel = PageViewModels[0];
        }

        #region Properties / Commands

        public ICommand ChangePageCommand
        {
            get
            {
                if (_changePageCommand == null)
                {
                    _changePageCommand = new RelayCommand(
                        p => ChangeViewModel((IPageViewModel)p),
                        p => p is IPageViewModel);
                }

                return _changePageCommand;
            }
        }

        public List<IPageViewModel> PageViewModels
        {
            get
            {
                if (_pageViewModels == null)
                    _pageViewModels = new List<IPageViewModel>();

                return _pageViewModels;
            }
        }

        public IPageViewModel CurrentPageViewModel
        {
            get
            {
                return _currentPageViewModel;
            }
            set
            {
                if (_currentPageViewModel != value)
                {
                    _currentPageViewModel = value;
                    OnPropertyChanged("CurrentPageViewModel");
                }
            }
        }

        #endregion

        #region Methods

        private void ChangeViewModel(IPageViewModel viewModel)
        {
            if (!PageViewModels.Contains(viewModel))
                PageViewModels.Add(viewModel);

            CurrentPageViewModel = PageViewModels
                .FirstOrDefault(vm => vm == viewModel);
        }

        #endregion
    }

This won’t compile right away because I’ve made some changes to it. For one, all my PageViewModels now inherit from an IPageViewModel interface so they can have some common properties, such as a Name.

I also created a new HomeViewModel and HomeView since its hard to demonstrate navigation unless you have at least 2 pages. The HomeViewModel is a blank class that inherits from IPageViewModel, and the HomeView is just a blank UserControl.

In addition, I added an s to ProductsViewModel since it really deals with multiple products, not a single one.

An added advantage to having a ViewModel to control the application state is that it can also be used to handle other application-wide objects, such as Current User, or Error Messages.

The View

I also need an ApplicationView for my ApplicationViewModel. It needs to contain some kind of Navigation that shows the list of PageViewModels, and clicking on a PageViewModel should execute the ChangePage command.

It also needs to contain a control to display the CurrentPage property, and I usually use a ContentControl for that. This allows me to use DataTemplates to tell WPF how to draw each IPageViewModel.

<Window x:Class="SimpleMVVMExample.ApplicationView"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:local="clr-namespace:SimpleMVVMExample"
        Title="Simple MVVM Example" Height="350" Width="525">

    <Window.Resources>
        <DataTemplate DataType="{x:Type local:HomeViewModel}">
            <local:HomeView />
        </DataTemplate>
        <DataTemplate DataType="{x:Type local:ProductsViewModel}">
            <local:ProductsView />
        </DataTemplate>
    </Window.Resources>

    <DockPanel>
        <Border DockPanel.Dock="Left" BorderBrush="Black" BorderThickness="0,0,1,0">
            <ItemsControl ItemsSource="{Binding PageViewModels}">
                <ItemsControl.ItemTemplate>
                    <DataTemplate>
                        <Button Content="{Binding Name}"
                                Command="{Binding DataContext.ChangePageCommand, RelativeSource={RelativeSource AncestorType={x:Type Window}}}"
                                CommandParameter="{Binding }"
                                Margin="2,5"/>
                    </DataTemplate>
                </ItemsControl.ItemTemplate>
            </ItemsControl>
        </Border>

        <ContentControl Content="{Binding CurrentPageViewModel}" />
    </DockPanel>
</Window>

In this example, I’m using an ItemsControl to display my PageViewModels. Each item is drawn using a Button, and the Button’s Command property is bound to the ChangePageCommand.

Since the Button’s DataContext is the PageViewModel, I used a RelativeSource binding to find the ChangePageCommand. I know that my Window is the ApplicationView, and it’s DataContext is the ApplicationViewModel, so this binding looks up the VisualTree for the Window tag, and gets bound to Window.DataContext.ChangePageCommand.

Also note that I am putting DataTemplates in Window.Resources to tell WPF how to draw each IPageViewModel. By default, if WPF encounters an object in it’s visual tree that it doesn’t know how to handle, it will draw it using a TextBlock containing the .ToString() method of the object. By defining a DataTemplate, I am telling WPF to use a specific template instead of defaulting to a TextBlock.

If you are continuing from the Simple MVVM Example, I moved the ProductView out of a ResourceDictionary and into a UserControl to make this simpler.

Starting the Example

The last thing to do is change App.xaml to make ApplicationView and ApplicationViewModel our startup, instead of ProductView/ProductViewModel.

public partial class App : Application
{
    protected override void OnStartup(StartupEventArgs e)
    {
        base.OnStartup(e);

        ApplicationView app = new ApplicationView();
        ApplicationViewModel context = new ApplicationViewModel();
        app.DataContext = context;
        app.Show();
    }
}

Run the project and you should see something that looks like the images below, which quickly switches the CurrentPage when clicking on the Navigation buttons.

Screenshot of HomeHome

Screenshot of ProductsProducts

Summary

And there you have it. A simple navigation example with MVVM.

You can download the source code for this sample from here.

Once you get more comfortable with WPF, I would recommend looking into using a Messaging System, such as MVVM Light’s Messenger, or Microsoft Prism’s EventAggregator to broadcast ChangePage commands from any ViewModel so you wouldn’t need to find the ApplicationViewModel to execute the ChangePageCommand, however that’s for another day.

<< Back – A Simple MVVM Example
>> Next – Communication between ViewModels


Navigating WPF’s Visual Tree

October 9, 2011

There are many times when I’ve needed to navigate up or down WPF’s Visual Tree from the code behind to find an object, so I finally decided to put all my visual tree navigation code into a single helper class.

For example, to find controls I can now use the following syntax:

// Search up the VisualTree to find DataGrid 
// containing specific Cell
var parent = VisualTreeHelpers.FindAncestor<DataGrid>(myDataGridCell);

// Search down the VisualTree to find a CheckBox 
// in this DataGridCell
var child = VisualTreeHelpers.FindChild<CheckBox>(myDataGridCell);

// Search up the VisualTree to find a TextBox 
// named SearchTextBox
var searchBox = VisualTreeHelpers.FindAncestor<TextBox>(myDataGridCell, "SeachTextBox");

// Search down the VisualTree to find a Label
// named MyCheckBoxLabel
var specificChild = VisualTreeHelpers.FindChild<Label>(myDataGridCell, "MyCheckBoxLabel");

And here’s the VisualTreeHelpers class

using System.Windows;
using System.Windows.Media;

namespace MyNamespace
{
    public class VisualTreeHelpers
    {
        /// <summary>
        /// Returns the first ancester of specified type
        /// </summary>
        public static T FindAncestor<T>(DependencyObject current)
        where T : DependencyObject
        {
            current = VisualTreeHelper.GetParent(current);

            while (current != null)
            {
                if (current is T)
                {
                    return (T)current;
                }
                current = VisualTreeHelper.GetParent(current);
            };
            return null;
        }

        /// <summary>
        /// Returns a specific ancester of an object
        /// </summary>
        public static T FindAncestor<T>(DependencyObject current, T lookupItem)
        where T : DependencyObject
        {
            while (current != null)
            {
                if (current is T && current == lookupItem)
                {
                    return (T)current;
                }
                current = VisualTreeHelper.GetParent(current);
            };
            return null;
        }

        /// <summary>
        /// Finds an ancestor object by name and type
        /// </summary>
        public static T FindAncestor<T>(DependencyObject current, string parentName)
        where T : DependencyObject
        {
            while (current != null)
            {
                if (!string.IsNullOrEmpty(parentName))
                {
                    var frameworkElement = current as FrameworkElement;
                    if (current is T && frameworkElement != null && frameworkElement.Name == parentName)
                    {
                        return (T)current;
                    }
                }
                else if (current is T)
                {
                    return (T)current;
                }
                current = VisualTreeHelper.GetParent(current);
            };

            return null;

        }

        /// <summary>
        /// Looks for a child control within a parent by name
        /// </summary>
        public static T FindChild<T>(DependencyObject parent, string childName)
        where T : DependencyObject
        {
            // Confirm parent and childName are valid.
            if (parent == null) return null;

            T foundChild = null;

            int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
            for (int i = 0; i < childrenCount; i++)
            {
                var child = VisualTreeHelper.GetChild(parent, i);
                // If the child is not of the request child type child
                T childType = child as T;
                if (childType == null)
                {
                    // recursively drill down the tree
                    foundChild = FindChild<T>(child, childName);

                    // If the child is found, break so we do not overwrite the found child.
                    if (foundChild != null) break;
                }
                else if (!string.IsNullOrEmpty(childName))
                {
                    var frameworkElement = child as FrameworkElement;
                    // If the child's name is set for search
                    if (frameworkElement != null && frameworkElement.Name == childName)
                    {
                        // if the child's name is of the request name
                        foundChild = (T)child;
                        break;
                    }
                    else
                    {
                        // recursively drill down the tree
                        foundChild = FindChild<T>(child, childName);

                        // If the child is found, break so we do not overwrite the found child.
                        if (foundChild != null) break;
                    }
                }
                else
                {
                    // child element found.
                    foundChild = (T)child;
                    break;
                }
            }

            return foundChild;
        }

        /// <summary>
        /// Looks for a child control within a parent by type
        /// </summary>
        public static T FindChild<T>(DependencyObject parent)
            where T : DependencyObject
        {
            // Confirm parent is valid.
            if (parent == null) return null;

            T foundChild = null;

            int childrenCount = VisualTreeHelper.GetChildrenCount(parent);
            for (int i = 0; i < childrenCount; i++)
            {
                var child = VisualTreeHelper.GetChild(parent, i);
                // If the child is not of the request child type child
                T childType = child as T;
                if (childType == null)
                {
                    // recursively drill down the tree
                    foundChild = FindChild<T>(child);

                    // If the child is found, break so we do not overwrite the found child.
                    if (foundChild != null) break;
                }
                else
                {
                    // child element found.
                    foundChild = (T)child;
                    break;
                }
            }
            return foundChild;
        }    
	}
}

Edit: Finally the link where I got the original code from. I’ve made some modifications to it, but the original code can be found here.


Navigation with MVVM

July 17, 2011

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