MVVMLight and ModernUI Part 2 - Navigation

MVVMLight and ModernUI Navigation

 

 

The first part of this series covered installing and configuring a very basic ModernUI-based WPF application that uses the MVVMLight Toolkit.  In this post, I’ll detail how to perform navigation between views “MVVM-style”, using the MVVMLight Messenger.  This pattern allows you to pre-initialize the ViewModel of a target view without needing to pass parameters, and follows the MVVM pattern of not having dependencies between ViewModels and their Views, which helps with testability and separation of concerns.

ModernUI Navigation

The primary challenge of ModernUI with MVVM is that the ModernUI framework was designed to be “pattern-agnostic”, and implements navigation in the View layer.  Views can implement an interface called “IContent” that allows them to intercept navigation events – but this can’t be implemented in theViewModel layer, which causes problems when you want to drive navigation from your ViewModel.

The solution is to use the MVVMLight Messenger to pass messages from the ViewModel to the View using publish/subscribe.

Register to Process Navigation Messages

1.       Create a Navigation message for MVVMLight to pass around.  This can be a simple class, with a couple of basic properties:

namespace ModernUI_MVVMLight.Messaging

{

    public class NavigationMessage

    {

        public string TargetPage { getset; }

    }

}

 

2.       In the constructor for View1.xaml.cs, register to receive navigation messages.  Note that this code could/should be put in some kind of base class for your User Controls, since it’s the same for every view.

public partial class View1

{

    public View1()

    {

        InitializeComponent();

 

        Messenger.Default.Register<NavigationMessage>(this, p =>

            {

                // Create a URI to the target page

                var uri = new Uri(p.TargetPageUriKind.Relative);

 

                // Find the frame we are currently in using the ModernUI "NavigationHelper" - THIS WILL NOT WORK IN THE VIEWMODEL

                var frame = NavigationHelper.FindFrame(nullthis);

 

                // Set the frame source, which initiates navigation

                frame.Source = uri;

            });

    }

}

Add a RelayCommand to the Source View

Both ModernUI and MVVMLight provide a RelayCommand implementation.  I use the MVVMLight version, just for consistency, but either one should work.

1.       Add the command handler to the ViewModel, View1ViewModel.cs:

    public class View1ViewModel

    {

        public View1ViewModel()

        {

            Info = "This is View 1";

 

            // Instantiate the RelayCommand.  This is much less verbose

            // than the default WPF Command declaration, and why

            // RelayCommands are nice to use.

            NavigateToView2Command = new RelayCommand(NavigateToView2);

 

        }

 

        // Info property, for a label to be shown on the view

        public string Info { getset; }

 

        // Declare the RelayCommand

        public RelayCommand NavigateToView2Command { getprivate set; }

 

        // Method to run when the command is executed

        public void NavigateToView2()

        {

            // TODO: IMPLEMENT NAVIGATION HERE

        }

    }

 

2.       Add a button to View1.xaml to fire this RelayCommand.  I switched the Grid to a DockPanel to allow for more content and positioning.  I’m also using the ModernUI “ModernButton”, which is a “Windows 8” styled control that comes with ModernUI.

<UserControl x:Class="ModernUI_MVVMLight.Views.View1"

             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"

             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"

             xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"

             xmlns:d="http://schemas.microsoft.com/expression/blend/2008"

             xmlns:mui="clr-namespace:FirstFloor.ModernUI.Windows.Controls;assembly=FirstFloor.ModernUI"

             mc:Ignorable="d"

             d:DesignHeight="300" d:DesignWidth="300"

             DataContext="{Binding View1, Source={StaticResource Locator}}">

    <DockPanel>

        <Label DockPanel.Dock="Top" Content="{Binding Info}" />

        <mui:ModernButton Content="Click" Command="{Binding NavigateToView2Command}" />

    </DockPanel>

</UserControl>

Initialize the ViewModel of the Destination View

Say you want to navigate to a second View and pass some data to it – you could pass parameters between views using ModernUI, but it’s a bit limiting since you can only pass text, and it also causes communication problems since we’re handling the command in the ViewModel.  To do this in an “MVVM-compliant” pattern, we first grab a reference to the ViewModel for the target view and so some work on it, then when we navigate to the view target view, it will bind to the already-initialized ViewModel.

        // Method to run when the command is executed

        public void NavigateToView2()

        {

            // Get a reference to View2's ViewModel from the IoC container

            View2ViewModel vm = SimpleIoc.Default.GetInstance<View2ViewModel>();

           

            // Set the Info string value to something different

            vm.Info = "Initializing This Text from View1ViewModel";

        }

Navigate to the Destination View using Messaging

To finish up the solution, all we need to do is fire off the NavigationMessage, which will be handled by the View and which will switch the frame to show View2.  View2 will pull it’s ViewModel out of the ViewModelLocator, which will be ready and waiting for it with the newly initialized text to display.

        // Method to run when the command is executed

        public void NavigateToView2()

        {

            // Get a reference to View2's ViewModel from the IoC container

            View2ViewModel vm = SimpleIoc.Default.GetInstance<View2ViewModel>();

           

            // Set the Info string value to something different

            vm.Info = "Initializing This Text from View1ViewModel";

 

            // Send the navigation message

            Messenger.Default.Send<NavigationMessage>(new NavigationMessage() {TargetPage = "Views/View2.xaml"});

        }


What’s Next

In the next post of this series, I’ll demonstrate setting up a DesignDataService, which is one of the nice features of MVVMLight.

No Comments