19

I've just started a new project in which the presentation layer will be done by WPF and MVVM Light by GalaSoft.

I need a lot of views and it's not clear to me how to manage navigation through windows.

First of all, the templates offered in MVVM Light for creating a new "WPF MVVM View" create a new Window that is not possible to use for navigation by frame (I mean, by putting a frame in mainView and changing the source path to navigate).

Do I simply have to change Window to Page for all the views I create using templates?

Or is there a different way to perform navigation in WPF with the MVVM Light toolkit?

Alex Angas
  • 59,219
  • 41
  • 137
  • 210
zero51
  • 801
  • 2
  • 8
  • 20

3 Answers3

24

I usually use a ContentControl to display dynamic content. It's Content property is usually bound to a CurrentViewModel property in the parent ViewModel, and DataTemplates are used to tell WPF how to draw the child ViewModels.

To change views, simply change the CurrentViewModel property in the parent ViewModel

You can find an example at this article of mine

Rachel
  • 130,264
  • 66
  • 304
  • 490
  • 1
    Hi Rachel, if you could post a minimal example of how to implement this in addition to your link, that'd be great. It just helps folks like me coming to this answer 5 years later in case the link no longer works for any given reason – Ortund Sep 12 '17 at 08:21
  • @Ortund Would [this SO answer](https://stackoverflow.com/a/12216068/302677) help? It's been a while since I wrote this answer, but I think the code at that other answer should be a suitable example. – Rachel Sep 12 '17 at 13:23
15

Eventually I did it this way.

Following the idea of o_q, I created NavigationWindow as MainWindow and changed all the the views to page.

Then, I created an inteface and a class which using Navigation:

    public interface INavigationService
    {
        event NavigatingCancelEventHandler Navigating;
        void NavigateTo(Uri pageUri);
        void GoBack();
    }

    public class NavigationService : INavigationService
    {
        private NavigationWindow _mainFrame;

        #region Implementation of INavigationService

        public event NavigatingCancelEventHandler Navigating;
        public void NavigateTo(Uri pageUri)
        {

            if (EnsureMainFrame())
            {
                _mainFrame.Navigate(pageUri);
            }

        }

        public void GoBack()
        {
            if (EnsureMainFrame()
                && _mainFrame.CanGoBack)
            {
                _mainFrame.GoBack();
            }

        }

        #endregion

        private bool EnsureMainFrame()
        {
            if (_mainFrame != null)
            {
                return true;
            }

            _mainFrame = System.Windows.Application.Current.MainWindow as NavigationWindow;

            if (_mainFrame != null)
            {
                // Could be null if the app runs inside a design tool
                _mainFrame.Navigating += (s, e) =>
                {
                    if (Navigating != null)
                    {
                        Navigating(s, e);
                    }
                };

                return true;
            }

            return false;
        }

    }

Then, in viewModelLocator I created all the const string nedded to store the paths to my views:

    public class ViewModelLocator
    {

        #region Views Paths

        public const string FrontendViewPath = "../Views/FrontendView.xaml";
        public const string BackendViewPath = "../Views/BackendView.xaml";
        public const string StartUpViewPath = "../Views/StartUpView.xaml";
        public const string LoginViewPath = "../Views/LoginView.xaml";
        public const string OutOfOrderViewPath = "../Views/OutOfOrderView.xaml";
        public const string OperativeViewPath = "../Views/SubViews/OperativeView.xaml";
        public const string ConfigurationViewPath = "../Views/SubViews/ConfigurationView.xaml";
        #endregion
     }

In App.cs, in the Application_Startup event handler, with the help of Unity IoC I registered a singleton of NavigationService:

    public partial class App : System.Windows.Application
    {

        private static IUnityContainer _ambientContainer;
        public static IServiceLocator AmbientLocator { get; private set; }

        ...

       private void Application_Startup(object sender, System.Windows.StartupEventArgs e)
        {          

           _ambientContainer =
               new UnityContainer();

           _ambientContainer.RegisterType<INavigationService, NavigationService>(new ContainerControlledLifetimeManager());

           AmbientLocator = new UnityServiceLocator(_ambientContainer);
           ServiceLocator.SetLocatorProvider(() => AmbientLocator);

Now, in my ViewModelLocator, I can register a "Galasoft" message to catch all the events and navigate to a page; in the constructor I have:

        public ViewModelLocator()
        {
            CreateMain();
            CreateFrontend();
            CreateBackend();
            CreateStartUp();
            CreateOperative();
            CreateLogin();
            CreateConfiguration();
            CreateOutOfOrder();


            // Set Startup Page...
            ServiceLocator.Current.GetInstance<INavigationService>().NavigateTo(new Uri(StartUpViewPath, UriKind.Relative));

            Messenger.Default.Register<MoveToViewMessage>(this, message =>
            {
                switch (message.StateInfo.StateType)
                {
                    case StateType.StartUpState:

                        ServiceLocator.Current.GetInstance<INavigationService>().NavigateTo(new Uri(StartUpViewPath,UriKind.Relative));
                        break;
                    case StateType.LoginState:
                        ServiceLocator.Current.GetInstance<INavigationService>().NavigateTo(new Uri(LoginViewPath, UriKind.Relative));
                        break;
                    case StateType.OperativeState:
                        ServiceLocator.Current.GetInstance<INavigationService>().NavigateTo(new Uri(OperativeViewPath, UriKind.Relative));
                        break;
                    case StateType.ConfigurationState:
                        ServiceLocator.Current.GetInstance<INavigationService>().NavigateTo(new Uri(ConfigurationViewPath, UriKind.Relative));
                        break;
                    case StateType.ClosedState:
                    case StateType.OutOfOrderState:
                        ServiceLocator.Current.GetInstance<INavigationService>().NavigateTo(new Uri(OutOfOrderViewPath, UriKind.Relative));
                        break;
                    default:
                        ServiceLocator.Current.GetInstance<INavigationService>().NavigateTo(new Uri(StartUpViewPath, UriKind.Relative));
                        break;
                }
            });

        }

In this way I keep all the viewModels "ignorant"... they don't know anything about navigation, plus I don't have code behind.

If I need to navigate by using a button from a view I can resolve NavigationService from the connected viewModel and navigate to the Page I need.

And, most important thing, it works!

Pavel Anikhouski
  • 21,776
  • 12
  • 51
  • 66
zero51
  • 801
  • 2
  • 8
  • 20
  • I noticed that ViewModelLocator constructor is not static in your example however it is static in default implementation... – User1551892 Jan 29 '14 at 08:55
0

For a navigable application, you'll want your start up view to be a NavigationWindow instead of a Window

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

    x:Class="MainWindow"
    Title="My Application Title"
    Height="300"
    Width="400" />

Code behind:

using System.Windows.Navigation;

public partial class MainWindow : NavigationWindow
{
    public MainWindow()
    {
        InitializeComponent();
    }
}

The MVVM Light view templates use Window, but as you have guessed, you can just change it. If you want to be able to navigate to and from this view, make it a Page. This is how you navigate:

<Page
    x:Class="Page1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    Title="Page1">
    <Grid>
        <!-- this button will navigate to another page -->
        <Button
            Content="Go to Page 2"
            Click="Button_Click" />
    </Grid>
</Page>

Code Behind:

using System.Windows;
using System.Windows.Controls;

public partial class Page1 : Page
{
    public Page1()
    {
        InitializeComponent();
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        // the Page class has a property "NavigationService" which allows you to navigate.
        // you can supply the "Navigate" method with a Uri or an object instance of the page 
        base.NavigationService.Navigate(new Page2());
    }
}
o_q
  • 81
  • 3
  • This could be a possibility, even if I don't like to put code in code behind. Moreover, I forgot to tell you that my application will be the human machine interface for vending machines, so it is crucial to me to have the possibility to navigate through pages in the easier way, for instance by sending events from the lower layers (the user cannot interact with the presentation layer because there is no mouse, no touch screen, no keyboard, ecc..). Is it possible to navigate the pages from ViewModel in this way? – zero51 Feb 16 '12 at 08:22
  • @zero51 on the issue of navigating in the viewmodel, I have posted a solution here: [How to navigate from one view to another view from viewmodel in silverlight?](http://stackoverflow.com/a/9304445/1130842) – o_q Feb 16 '12 at 15:30
  • I found this post in galasoft site [link](http://blog.galasoft.ch/archive/2011/01/06/navigation-in-a-wp7-application-with-mvvm-light.aspx). It explain how to implement navigation in Windows phone 7... I'll try to implement it in WPF using navigation. – zero51 Feb 17 '12 at 08:07
  • @Zero51: Did you manage to fix this problem. I am looking an example as well and have been unable to do so? – user101010101 Nov 26 '12 at 17:01
  • @user101010101: I implemented the navigation as I explained in my answer. It worked like a charm! – zero51 Nov 30 '12 at 13:03