WPF page navigation like in MVC part 2: The MVVMC Framework

Before reading this, check out my last post. I talked about navigation techniques in MVVM and how I didn’t like them too much. I also talked about learning Asp.NET Core is and how good the navigation / routing system is in there.

So, following the pain, I decided to create a lightweight navigation WPF framework similar to the one in Asp.NET Core. We’re still using MVVM, but adding controllers which makes it MVVMC, Model-View-ViewModel-Controller (This is how the library is called as well).

Everything is available on GitHub.
Now let’s see how to use MVVMC with a WPF application:

Adding the MVVMC library to the Application

If you’ve downloaded the solution from GitHub, then you already have a “MainApp” which references to MVVMC. The solution should look like this:

 Adding a Region

A Region is a Control with dynamic content. This is the part whose content you want changing when navigating. In my MainWindow:

The bottom of my window is the Region. This part is dynamic. The content of the Region can be changed and defined by a Controller (MainOperationController). A Controller controls only one Region. And a Region is controlled by only one Controller.

The top part of the window, with two buttons is static and never changes.

Initialization

For MVVMC to work, we need to initialize the NavigationService with the Pages namespace. So in App.xaml.cs:

The Pages namespace is the namespace of all our Controllers, Views and ViewModels – See next.

“Pages” directory and File structure

All “Pages” should be held in one namespace & directory in the solution.
The directory looks like this:

We see that all controllers, Views and ViewModels are in a single Pages directory (It can be named differently). Each inner directory represent a Controller / Region pair. Name of the directory does matter. The controller must be named [DirectoryName]Controller.cs. So for directory “MainOperation” the controller must be “MainOperationController” and the Region must set ControllerID=”MainOperation”

We need to keep the namespaces same as the directory structure. So the namespace of “AboutViewModel” should be MainApp.Pages.MainOperation.

Views

A view should be a FrameworkElement. So any User Control or a Custom Control will work. No need to derive from anything.

The only rule is that the name of the view should end with “View”.

A View and ViewModel pair is called a Page. So “AboutView” and “AboutViewModel” together make the page “About”.

ViewModels

A ViewModel should derive from MVVMCViewModel. Like this:

Another option is to derive from MVVMCViewModel<TController>. Like this:

Controllers

Here’s my MainOperationController example:

  • A controller class should be called [ControllerID]Controller
  • A Controller should derive from MVVMC.Controller abstract class.
  • A Controller is connected to a single Region. When this Region is loaded, the controller is created and navigation to Initial is requested. Each controller should implement InitialI() method.
  • Each navigation request invokes a Method with the same name in the controller. So when navigation to “About”   controller.Navigate(“About”, parameter: null);
    The “About()” method will be invoked.
    “About” is called the Action.
  • Optionally a navigation method can accept an object as a parameter.
  • When calling “ExecuteNavigation” from a method, the controller will create a View and ViewModel with the same name as the method.

    This code will:
    1) look for AboutView and AboutViewModel in the same namespace as the controller
    2) Create instances of them
    3) Set View.DataContext = ViewModel
    4) Change the Content of the relevant Region to be the View

Navigation options

From View:

You can use a special type of Command called NavigationCommand. Like this:

Pressing on About will find a controller called “MainApp.Pages.MainOperation.MainOperationController” and invoke the method About().

As you see, the Home Command doesn’t have an Action property set. This will bring us to Initial action.

From ViewModel:

Each ViewModel derives from MVVMCViewModel base class which exposes several methods for navigation:

  • GetController() will return the current control the ViewModel is related to. For example:
  • NavigationService is a singleton and you can use it from each ViewModel:
    Using NavigationService this way is available from anywhere in the application.

Navigating with parameter

All navigation methods allow to pass a parameter of type object. Here’s an example:
In ViewModel:

In Controller:

 

ViewBag

A controller can populate the navigation target ViewModel with data in 2 ways: A parameter and a ViewBag. The ViewModel base MVVMCViewModel holds them as properties:

We can use the “NavigationParameter” and the “ViewBag” in Initialize() method. Or, there’s a special kind of binding available ViewBagBinding which automatically binds to the ViewBag. But it’s a simple “OneTime” binding (for now).
In View:

 

Regions inside Regions

We might have hundreds of screens in our App and we might want different controllers to control these screen. For example we can have a “main” controller and a controller for a “mini-flow”. This can be achieved by adding another Region in a View. In the sample application we have a “MainOperationController” and one of the views introduces another Region which is controlled by a different Controller:

AllEmployeesView in MainOperation:

The View contains another Region. When loaded, AllEmployeesController will be called with Initial()

Since Initial calls SelectEmployee(), the Region’s content will become “SelectEmployeeView”

 

This finishes the technical overview. If you are curious, download the sample app from GitHub and run it. I’d love to hear your thoughts.

What more is missing?

There are still some important navigation features missing. Some are:

  • Back and Forward navigation.
  • CanNavigate and CanNavigateBack functionality.
  • Allowing more flexible structure of Controllers, Views and ViewModels. Some ideas are:
    1. Resolving Controllers by Type instead of by ID. This will allow different namespaces.
    2. Include an IResolver and allow each project to implement it differently.
  • Allow Views and Controllers to live in multiple assemblies.
  • A NuGet package and or template.

If there’s interest in this project I promise to add the missing functionality!

Is the MVVMC design pattern already being used?

I found out MVVMC (the design pattern, not my project) is already out there and some development teams are adopting it – Check out this blog post for example MVVM is dead, long live MVVMC!. I couldn’t find any WPF frameworks, so everyone who uses it probably wrote their own custom code. However, I did find a WinRT framework for MVVMC – ControllerRT.

ControllerRT is available on GitHubI didn’t run it but I did look over the code. It looks good and somewhat similar to what I did. But also with many differences. For example there’s one controller for each View and ViewModel.

Summary

We saw how we can use a lightweight MVVMC framework for navigation in a WPF project. This provides a nice abstraction. Now, the View and ViewModel of a specific screen aren’t responsible for the navigation logic to a different screen.

I think I’ll use this in my future WPF projects, and hope some of you will try it as well. If you do try it, I’d love to hear feedback at michaels9876@gmail.com or by comments in this post.

I’d like to thank Aleksey Kravetz for sitting with me on the project and helping me with some of the concepts of this project!

MVVMC for WPF on GitHub.

2 thoughts on “WPF page navigation like in MVC part 2: The MVVMC Framework

  1. Pingback: WPF page Navigation like in MVC - Building an MVVM framework with Controllers - Michael's Coding Spot

Leave a Reply