Have you ever considered how WPF does navigation between pages with MVVM?
You’ve got your View and ViewModel, which present a single page, or some area in your page. But what’s the guideline to actually change the content of the page to a new View and ViewModel?
I think MVVM doesn’t really explain that. Which is strange because in almost any application you’d have multiple pages and some sort of flow.
And what about wizard-type applications? What is supposed to happen when I click that “Next” button?
Recently I worked on an ASP.NET Core project and I discovered Microsoft did a very good job implementing navigation with MVC. It’s extremely intuitive and easy to understand.
So after mulling over this for a while, I did the only reasonable thing anyone in my situation would do. I went ahead and built a framework in WPF to implement navigation like in ASP.Net Core.
But before getting into all that crazy talk about building frameworks and whatnot, let’s see some basic solutions for navigation in WPF and why I think MVC does this better.
Basic Navigation in WPF
There are two types of approaches to navigation in WPF
- View First
- View Model First
View First says that when changing some area with a new content, you’ll create a new UI Element, let’s say a UserControl, and replace the content of that area with the new User Control.
View Model First is similar but you are creating a new ViewModel, and replacing the content of the area with the ViewModel itself. WPF will know which View should be presented with that ViewModel and to create the appropriate connection between them
View.DataContext = ViewModel
How does WPF know to pair them?
Well, you should create DataTemplate resources without x:Key that connect between ViewModels and Views. Place them in App.xaml and that’s it.
View Model First seems to be more popular these days. Well, it does have advantages. For one, you can navigate from the ViewModel, dealing only with data. You can also populate the target ViewModel with whatever data you want before displaying it.
Here’s a very good post on implementing View Model First navigation:
So, what’s so bad about that?
It’s not bad per se. It’s pretty good considering MVVM doesn’t do anything to help us with the navigation process. However, here’s what I don’t like about this approach:
- The current ViewModel itself decides on the navigation. Meaning it decides on the ViewModel to be presented next.I don’t like it very much. The current ViewModel shouldn’t know about the Context of the application. What if the application cannot perform that navigation because it’s “in the middle” of something? The ViewModel shouldn’t know that logic.In fact, why does my ViewModel create the target ViewModel? Why does it even know the other ViewModel exists? This breaks the single responsibility principle.
- As seen in the example, setting up this small navigation logic took some work. And it will not end with that. What if we want to be able to navigate from the ViewModel, not by Command from the View?
What if we want to be able to change multiple areas in our Window? (Sidebar, header, footer, etc.). This is all possible, but requires a lot of work.
Let’s see how Microsoft handles this with MVC.
Navigation in MVC 6 / ASP.NET Core
In MVC 6, navigation and routing is done with a “by convention” approach. Like this:
Every time you go to a Url, you are routed to a Controller and an Action.
The routing is configurable, but usually it’s in this format:
For the Url
The controller is home and the action is music. Of course, you must provide a default controller and a default action.
When navigating to this Url, MVC will find a controller class called HomeController.cs, and invoke the method Music.
When the method returns View(), the program will automatically go to Music.cshtml in a directory called Views\Home\Music.cshtml (Views\[controller name]\[action name])
It appears Microsoft decided to do everything by convention.
This approach was a bit strange to me at first… I’m a programmer, I don’t like magical behavior.
But, while working on my project, I started realizing how incredibly convenient this is.
I really didn’t have to do anything myself. The routing system did everything for me.
How about doing the same in WPF?
As it turns out, it’s very possible to do just that with WPF. With the power of reflection, it’s not even that difficult.
As I’m writing this, I’m in the middle of developing a framework very similar to MVC, but with MVVM. I call it MVVMC 🙂
So, how will this MVVMC work exactly? How will the code look?
Well, I won’t show everything in this post, I’ll just give you a peek 🙂
MVVMC – A peek
First, we will define a Region that can be changed by navigation. It can be the entire Window.
We should tie the region to a ControllerID. Each region is controlled by a single controller. And a controller controls just one Region.
Then, we create our Controllers, Views and ViewModels, by convention, like in MVC.
- Each directory represents a single Controller.
In the picture we see the Views, ViewModels and controller for “MainOperation” controller.
- The controller should be called same as the directory name + “Controller”
- The views and ViewModels should be called with the same name but postfix “View” and “ViewModel”
To actually navigate, there are several options. One of them is with a built-in command. So, from any XAML:
When clicking on this button, a method called “About” in MainOperationController.cs will be called:
“ExecuteNavigation()” is similar to “return View()” in MVC 6. It does the following:
- Finds a View and ViewModel called “AboutView” and “AboutViewModel” in a directory called “MainOperation” and creates a new instance of them.
- Connects the View and the VM by assigning View’s DataContext to the ViewModel.
- Finds the Region relevant to current Controller and replace its Content with the new View.
Isn’t that a huge improvement to navigation solutions we have today?
The initialization is really easy as well.
We have to reference to MVVMC project and call an Initializie() method with the namespace of our Pages folder as parameter.
After that, we just create Views, ViewModels and Controllers as we please.
Releasing MVVMC and final thoughts
It seems that writing such a framework, small as it might be, is not that easy. I keep getting new ideas, changing stuff and just rewriting everything.
I’m in the final stages of building the MVVMC framework right now. Once ready I’ll place it on GitHub and write a post about it.
Anyway, I just remembered I want to rename some classes, change the folder structure and all the naming conventions. I better get back to it.