Out of the box, applications built using WinUI / Windows App Sdk or the Uno Platform have access to a wide range of controls, such as Frame, TabBar and NavigationView that can be used for navigation. However, there’s no common pattern/metaphor that makes it navigate within an application. Uno.Extensions includes Navigation which provides a consistent abstraction for navigation either in code (both codebehind and in Model/ViewModel) or XAML. In this post I’m going to start by giving a simple example of how Navigation works and then discuss the concept of Regions.
We’ll kick of this post by creating a new application using the Uno Platform Template Wizard. If you just want to build for Windows, you can just pick Windows from the Platforms tab in the Wizard. For the purpose of this post we’ll go with the Recommended preset, which will create a XAML based Material themed application that uses both MVUX (a Reactive pattern for state management) and region based navigation (i.e. Uno.Extensions with Navigation).
If we run up the generated application, clicking the button will navigate from MainPage to SecondPage.
Unlike an application that you’d get if you used the Blank App template for WinUI/Windows App SDK, if you look in App.xaml.cs you won’t see the creation of a Frame or explicit calls to Navigate to MainPage. If you look inside ShellModel you’ll see there’s a Start method that’s invoked from the constructor, which invokes the NavigateViewModelAsync
method, specifying the MainModel.
public async Task Start()
{
await _navigator.NavigateViewModelAsync<MainModel>(this);
}
I’m not going to go into detail right now about ViewMap and RouteMap but if you look in App.xaml.cs you’ll see there are three ViewMap instances defined that match pages and models. In this case MainModel
is associated with MainPage
, which is how Navigation knows to navigate to the MainPage
from the call to NavigateViewModelAsync
.
So the next question is, where’s the Frame that’s being used to navigate between MainPage
and SecondPage
. One of the built in capabilities of Navigation is that if you attempt to navigate to a Page
but there’s no Frame
, a new Frame
will be created and used to navigate. We’ll come back to this when we look at how NavigationView
works but in the context of a new application, a Frame
is created and used for navigation.
The follow up question is how does this newly created Frame get added to the application. To answer this, let’s step through the startup sequence for the application.
- An instance of the App class gets created
- The overridden App.OnLaunched method is invoked, which kicks of the initialization sequence for the application.
- An
IApplicationBuilder
instance is create by calling CreateBuilder and then a sequence of extension methods is invoked to register services etc. Note that these extension methods are very quick to execute because they don’t do much other than to register other callbacks for when Build is invoked. - There are a couple of extension methods invoked on
MainWindow
to enable hot reload (in DEBUG) and to set the Window icon - The NavigateAsync method is called on the
IApplicationBuilder
, specifying the UIElement that should be used as the root element of the application, in this caseShell
. - Inside the
NavigateAsync
method, a new instance ofShell
will be created and set as theContent
for theWindow
of the application. At this point the Window is activated so that the application appears and shows the progress indicator that’s part of theShell
. Build
is invoked on theIApplicationBuilder
, which callsBuild
on internally heldIHostBuilder
in order to create theIHost
instance. Note that this is done after the instance ofShell
is created to avoid any delays in displaying the application.- After the
IHost
instance has been created, an instance ofShellModel
is created (as this is the first RouteMap defined for the application), which in turn calls NavigateViewModelAsync to navigate to theMainModel
. - As there’s currently no Frame available, a new Frame is created and added to the
Content
property of theContentControl
specified by theShell
. In this case the ExtendedSplashScreen is returned – once all the startup logic has completed, the ExtendedSplashScreen will change states from displaying theLoadingContent
, to displaying theContent
- The Navigate method on the Frame is invoked in order to navigate to the
MainPage
As you can see from this sequence, there’s quite a few steps that are abstracted away through the use of Navigation, coupled with the Shell / ExtendedSplashScreen control.
Navigating from MainPage
to SecondPage
can be done a number of different ways. When the application is created from the template, the navigation is invoked by calling the NavigateViewModelAsync
method via code in the MainModel
(1 in the code below).
public async Task GoToSecond()
{
var name = await Name;
// 1 - Navigate based on Model/ViewModel
await _navigator.NavigateViewModelAsync<SecondModel>(this, data: new Entity(name!));
// 2 - Navigate to a view
await _navigator.NavigateViewAsync<SecondPage>(this, data: new Entity(name!));
// 3 - Navigate based on Data
await _navigator.NavigateDataAsync(this, new Entity(name!));
}
Other alternative methods are NavigateViewAsync
, where you can specify the type of view (in this case SecondPage
) that you want to navigate to, and NavigateDataAsync
where you specify an data instance and Navigation works out which view to navigate to based on the registered ViewMap instances.
You can also invoke navigation from either the codebehind or in XAML. Here’s an example of both:
Codebehind
private async void NavigateSecondPageClick(object sender, RoutedEventArgs e)
{
await this.Navigator().NavigateViewModelAsync<SecondModel>(this);
}
XAML
<Button Content="Go to Second Page"
uen:Navigation.Request="Second" />
As you can see from these code examples the navigation methods exist on an INavigator
instance that is either passed into the constructor of a Model/ViewModel, or can be retrieved using the Navigator()
extension method. You can think of an INavigator
as being the controller for navigation for a particular Region. A Region is a logical abstraction for an element within the application responsible for navigating between views. In this case the Frame that was created during startup is a Region and there is a corresponding FrameNavigator
instance that is used to control, and respond to, navigation on the Frame. It’s the FrameNavigator
instance that’s injected into the MainModel
constructor which is subsequently used to navigate to SecondPage
.
In this post we’ve covered how Navigation works during application startup and discussed in brief the concept of a Region. I’ll continue this discussion in the next post where we look in more detail at other types of Regions and how they relate to each other.
2 thoughts on “Navigation using Regions in Windows and Uno Platform Applications”