What’s a Navigation Region in Windows and Uno Platform Applications?

In all but the simplest single page application, the layout of an application has to be dynamicaly changed in order to display information, or capture input from the user. Most application frameworks provide a variety of components to make this easy. For example in a WinUI / Windows App SDK application can use a Frame to navigate between pages. Alternatively, a Flyout or ContentDialog can be used to display information to the user, or perhaps a NavigationView or TabBar can be used to switch content on a single page. All of these are examples of what can collectively be categorised as navigation. In this post we’re going to dig into the concept of navigation, with particular focus on the Navigation component that’s part of Uno.Extensions and how it provides a region based abstraction for navigation in Windows and Uno Platform applications.

My previous post gave a simple definition of a region as being an abstraction for any element in the application that’s capable of navigating (ie displaying) a view. The default (recommended) preset in the Uno Platform Solution Wizard uses a Frame to navigate between two pages but there are other types of regions. Regions are linked in a hierarchy that matches the hierarchy found in the visual tree within the application. As we get into discussing how routes are processed, the importance of this hierarchy will become evident.

The other thing to know about regions is that they have a corresponding INavigator implementation. The region itself, doesn’t include any control specific implmentation, instead it’s the corresponding INavigator that contains all the smarts to navigate with the specified control.

Here’s a quick list of the supported element types and the corresponding navigator:

  • Frame – FrameNavigator – Navigates forward to new pages, or back to previous pages. The FrameNavigator also responds to when the Frame navigates, which means navigation can be initialised by the Frame. For example, on mobile device, a swipe will navigate to a previous page on a Frame, without the developer writing any navigation logic.
  • ContentControl – ContentControlNavigator – Navigates by creating a new instance of the view and setting it as the Content on the ContentControl. Note that if the view is a page, a Frame is injected, along with a corresponding FrameNavigator, and used to navigate to the page. This means that if the page then attempts to navigate to another page, it will automatically have a back stack, making it possible to navigate back to the original page.
  • ContentDialog – ContentDialogNavigator – If the view that you’re navigating to inherits from ContentDialog, the navigation will be treated as a dialog navigation (as if you’d prefixed the navigation route with the ! prefix) and the view will be displayed as a ContentDialog.
  • FlyoutFlyoutNavigator – If the view that you’re navigating to inherits from Flyout, the navigation will be treat as a dialog navigation. Alternatively, you can prefix any navigation with ! and it will be treated as a dialog navigation with the view hosted inside a Flyout.
  • MessageDialogMessageDialogNavigator – If the navigation route is a predefined MessageDialog, or you’ve used the MessageDialog navigation extension methods, a MessageDialog will be displayed.
  • NavigationView – NavigationViewNavigator – A selector based navigator where the navigation route will select a menu item in the NavigationView. Selector based navigators also listen for changes on the control and will initiate a new navigation based on the selected item.
  • TabBar – TabBarNavigator – A selector based navigator (similar to NavigationViewNavigator)
  • Panel – PanelVisibilityNavigator – Navigates by hiding and showing children of the Panel based on the Name assigned to them. If there is no child that matches the navigation route, and a view can be determines from the route (either from a RouteMap, or via reflection) a new instance will be created and added to the Panel. Again, if the view is a Page, a Frame will be injected first, and use to navigate to the Page. Note that this Navigator is not registered for implicit use, meaning that just because you nominate a Panel as a navigation Region (by adding the Region.IsAttached="true" attached property to the Panel), it won’t use the PanelVisibilityNavigator. To use this Navigator, you need to also set the Region.Navigator="Visibility"
  • Popup – PopupNavigator – Navigates by hiding or showing the corresponding popup
  • DrawerControl – DrawerControlNavigator – Navigaes by hiding or showing the corresponding DrawerControl.

The base class, Navigator, is also a valid Navigator and will be used if no other Navigator can be resolved. For example if you set Region.IsAttached="true" on a Panel, by default the Navigator class will be used. When the base Navigator class is used directly, it exhibit a slightly different behavior, acting to combine, or link, nested Regions. You can think of the Navigator acting as a composite Navigator, meaning that any navigation request that is set to it, will be forwarded to the Navigator instances for the child Regions.

An example of this composite behavior can be found in the Uno Platform documentation for Uno.Extensions Navigation that shows how to use the TabBar control (see example). Here’s a reduced snippet of the XAML.

<Page x:Class="UsingTabBar.Views.MainPage" ... >
    <Grid uen:Region.Attached="True">
        <Grid uen:Region.Attached="True"
              uen:Region.Navigator="Visibility">
            <Grid uen:Region.Name="One"  Visibility="Collapsed">
                <TextBlock Text="One" />
            </Grid>
            <Grid uen:Region.Name="Two"  Visibility="Collapsed">
                <TextBlock Text="Two" />
            </Grid>
            <Grid uen:Region.Name="Three" Visibility="Collapsed">
                <TextBlock Text="Three" />
            </Grid>
        </Grid>
        <utu:TabBar  uen:Region.Attached="True">
            <utu:TabBar.Items>
                <utu:TabBarItem uen:Region.Name="One" />
                <utu:TabBarItem uen:Region.Name="Two" />
                <utu:TabBarItem uen:Region.Name="Three" />
            </utu:TabBar.Items>
        </utu:TabBar>
    </Grid>
</Page>

The first Grid is set as a Region and will use the Navigator. The second Grid is set as a Region and will use the PanelVisibilityNavigator. The TabBar is set as a Region and will use the TabBarNavigator. Navigation request set to the Navigator will be forwarded to the other two Navigators. When the selected TabBarItem is changed, a navigation request is forwarded up to the Navigator which is then sent back to both child Navigators (the TabBarNavigator ignores the request since the correct TabBarItem is already selected).

In this post we’ve started to touch on the different Navigators and how they’re used within the Region based navigation system provided by Uno.Extensions. In subsequent posts we’ll look at more examples for the different Navigators.

Leave a comment