In this article we describe a minimal framework for a navigation service in a WinUI 3 Desktop application on top of a NavigationView control. We will cover
- navigating from a menu item to an application page,
- navigating to a menu item from code behind,
- retrieving the current menu item,
- hiding and showing menu items, and
- dynamically adding menu items.
Our purpose is to describe the interaction between some of the core classes and come up with a pattern that you can reuse in your own WinUI 3 apps. For that reason we deliberately stay away from MVVM and Dependency Injection libraries. We created a small sample app, here’s how it looks like:
The app is a WinUI 3 Desktop app built on top of the new Windows App SDK v0.8 (previously known as Project Reunion) with the regular Visual Studio 2019 (no preview stuff needed). From their own documentation, we learn that
the Windows App SDK is a set of new developer components and tools that represent the next evolution in the Windows app development platform. The Windows App SDK provides a unified set of APIs and tools that can be used in a consistent way by any desktop app on Windows 11 and downlevel to Windows 10, version 1809,
and
Windows UI Library (WinUI) 3 is a native user experience (UX) framework for building modern Windows apps. It ships independently from the Windows operating system as a part of Project Reunion (now called the Windows App SDK). The 0.8 Preview release provides Visual Studio project templates to help you start building apps with a WinUI 3-based user interface.
Check this link on how to prepare your development environment for this. When all prerequisites are met, you should be able to create new WinUI 3 projects:
UWP developers will feel at home in a WinUI 3 Desktop application: it looks like UWP and it feels like UWP, except that
- the solution currently (and temporarily) comes with a separate MSIX installation project, and
- the main page (our Shell) is not a Page but a Window – one that supports both UWP and Win32 app models.
The main beef of our Shell Page Window is the WinUI 3 version of the NavigationView control. In the first releases of UWP we developers needed to create a navigation UI from scratch. In a couple of years modern XAML navigation UI evolved from DIY SplitView-based implementations (been there, done that) to simply putting a full fledged NavigationView control on the main page. NavigationView comes with different modes (left menu/top menu), built-in adaptive behavior, two-level hierarchical menu structure, footer menu items, back button support, animations, different icon types, … Apart from the menu, the control also comes with a Header, and a Frame to host the application pages.
Here’s the main structure of the Shell page in our sample app:
<NavigationView x:Name="NavigationView" Loaded="NavigationView_Loaded" SelectionChanged="NavigationView_SelectionChanged" Header="WinUI 3 Navigation Sample" IsBackButtonVisible="Collapsed" IsSettingsVisible="False"> <NavigationView.MenuItems> <NavigationViewItem Content="Home" Tag="XamlBrewer.WinUI3.Navigation.Sample.Views.HomePage" ToolTipService.ToolTip="Home"> <NavigationViewItem.Icon> <BitmapIcon UriSource="/Assets/Home.png" ShowAsMonochrome="False" /> </NavigationViewItem.Icon> </NavigationViewItem> <!-- More items --> </NavigationView.MenuItems> <NavigationView.FooterMenuItems> <NavigationViewItem Content="About" Tag="XamlBrewer.WinUI3.Navigation.Sample.Views.AboutPage"> <NavigationViewItem.Icon> <BitmapIcon UriSource="/Assets/About.png" ShowAsMonochrome="False" /> </NavigationViewItem.Icon> </NavigationViewItem> </NavigationView.FooterMenuItems> <Frame x:Name="ContentFrame" /></NavigationView>
The Festivals item demonstrates hierarchical navigation using nested menu items:
<NavigationViewItem Content="Festivals" Tag="XamlBrewer.WinUI3.Navigation.Sample.Views.FestivalPage" ToolTipService.ToolTip="Festivals"> <NavigationViewItem.MenuItems> <NavigationViewItem Content="Tomorrowland" Tag="XamlBrewer.WinUI3.Navigation.Sample.Views.FestivalDetailsPage" ToolTipService.ToolTip="Tomorrowland" /> <NavigationViewItem Content="Rock Werchter" Tag="XamlBrewer.WinUI3.Navigation.Sample.Views.FestivalDetailsPage" ToolTipService.ToolTip="Rock Werchter" /> </NavigationViewItem.MenuItems></NavigationViewItem>
There’s much more on NavigationView than we cover in this article. For more details check these guidelines and play around with the WinUI 3 Controls Gallery app:
Our navigation pattern assumes/enforces that all navigation in the app is initiated by the NavigationView instance in the Shell window. We believe that this is applicable to a huge number of apps – at least to the ones that we are currently migrating from UWP. All navigation requests must refer to a NavigationViewItem instance that corresponds with an entry in the menu. The menu items define the target page in their Tag and Content fields, as you saw in the XAML snippets above. It’s the SelectionChanged event that triggers the navigation:
private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args){ SetCurrentNavigationViewItem(args.SelectedItemContainer as NavigationViewItem);}
This first call into our micro-framework looked up the selected menu item and updated the content frame. Here’s how the code is structured:
- All navigation-related code is implemented in a partial class of the Shell window,
- encapsulated in an interface that is
- exposed via the App instance to
- the different XAML pages.
When a menu item is selected, we look up the target page information from that menu item, and pass it to Frame.Navigate(). We set the appropriate page header and update the menu’s SelectedItem. That last line is needed in case the navigation was triggered from code behind.
public void SetCurrentNavigationViewItem(NavigationViewItem item){ if (item == null) { return; } if (item.Tag == null) { return; } ContentFrame.Navigate(Type.GetType(item.Tag.ToString()), item.Content); NavigationView.Header = item.Content; NavigationView.SelectedItem = item;}
Feel free add a test to prevent navigating to an invisible menu item and some exception handling, if you want.
To avoid showing an empty content frame, the app auto-navigates to the Home page when the app starts. The navigation logic is the same throughout all use cases in the app:
- look up the menu item that corresponds to the target page, and
- use it in the SetCurrentNavigationViewItem() call.
private void NavigationView_Loaded(object sender, RoutedEventArgs e){ // Navigates, but does not update the Menu. // ContentFrame.Navigate(typeof(HomePage)); SetCurrentNavigationViewItem(GetNavigationViewItems(typeof(HomePage)).First());}
GetNavigationViewItems() retrieves a flattened list of all menu items of the NavigationView: the MenuItems, the FooterMenuItems, and their children. We added two overloads – one to filter on page type (e.g. to find all detail pages in a list) and another to filter on page type and title (to find a specific detail page):
public List<NavigationViewItem> GetNavigationViewItems(){ var result = new List<NavigationViewItem>(); var items = NavigationView.MenuItems.Select(i => (NavigationViewItem)i).ToList(); items.AddRange(NavigationView.FooterMenuItems.Select(i => (NavigationViewItem)i)); result.AddRange(items); foreach (NavigationViewItem mainItem in items) { result.AddRange(mainItem.MenuItems.Select(i => (NavigationViewItem)i)); } return result;}public List<NavigationViewItem> GetNavigationViewItems(Type type){ return GetNavigationViewItems().Where(i => i.Tag.ToString() == type.FullName).ToList();}public List<NavigationViewItem> GetNavigationViewItems(Type type, string title){ return GetNavigationViewItems(type).Where(ni => ni.Content.ToString() == title).ToList();}
Feel free to filter away NavigationViewItemHeader and NavigationViewItemSeparator instances from the flat list, if they’re in your way.
We also disclose the currently selected menu item:
public NavigationViewItem GetCurrentNavigationViewItem(){ return NavigationView.SelectedItem as NavigationViewItem;}
The previous methods were all implemented in the Shell Window. To make them available all over the app, we first defined them in an interface:
public interface INavigation{ NavigationViewItem GetCurrentNavigationViewItem(); List<NavigationViewItem> GetNavigationViewItems(); List<NavigationViewItem> GetNavigationViewItems(Type type); List<NavigationViewItem> GetNavigationViewItems(Type type, string title); void SetCurrentNavigationViewItem(NavigationViewItem item);}
Then we exposed the implementation via the App instance – it knows the Shell because it creates it on start-up:
private Shell shell;public INavigation Navigation => shell; protected override void OnLaunched(LaunchActivatedEventArgs args){ shell = new Shell(); shell.Activate();}
All parts of the code base can now easily access the lightweight Navigation Service:
(Application.Current as App).Navigation
Showing and hiding existing menu items
Our sample app has a ‘Beer’ page that only becomes visible when the user confirms she’s old enough to handle its content. The page is defined in the NavigationView menu, but is initially invisible. The Home page has a checkbox in the lower right corner:
When the box is checked, the hidden menu item appears:
Here’s the code in the Homepage. It looks up the BeerPage NavigationViewItem, and manipulates its Visibility:
private static NavigationViewItem BeerItem => (Application.Current as App).Navigation.GetNavigationViewItems(typeof(BeerPage)).First();private void CheckBox_Checked(object sender, RoutedEventArgs e){ BeerItem.Visibility = Visibility.Visible;}private void CheckBox_Unchecked(object sender, RoutedEventArgs e){ BeerItem.Visibility = Visibility.Collapsed;}
Programmatically navigating to an existing menu item
The FormulaOnePage in our sample app has a hyperlink to the FestivalPage:
The code behind that hyperlink looks up the target menu item using the GetNavigationViewItems overload with the page type, and then navigates to it – very straightforward:
private void Hyperlink_Click(Hyperlink sender, HyperlinkClickEventArgs args){ var navigation = (Application.Current as App).Navigation; var festivalItem = navigation.GetNavigationViewItems(typeof(FestivalPage)).First(); navigation.SetCurrentNavigationViewItem(festivalItem);}
There’s a similar hyperlink in the HomePage, to test whether we can reach footer menu items in the same way:
Under the Festival menu item, there is a list of FestivalDetails pages – all of the same type, but with a different topic of course. The hyperlinks on that Festival page use the GetNavigationViewItems overload with page type and content, and also ensure that the parent (Festival) menu item gets expanded:
private void Hyperlink_Click(Hyperlink sender, HyperlinkClickEventArgs args){ var navigation = (Application.Current as App).Navigation; navigation.GetCurrentNavigationViewItem().IsExpanded = true; var festivalItem = navigation.GetNavigationViewItems(typeof(FestivalDetailsPage), "Rock Werchter").First(); navigation.SetCurrentNavigationViewItem(festivalItem);}
Here’s one of the detail pages:
Dynamically adding menu items
The BeerPage has a button to programmatically add BeerDetailPage items:
It looks up the parent, adds a menu item of the appropriate type and with its specific title, and makes sure that the parent is expanded:
private void Button_Click(object sender, RoutedEventArgs e){ var beerItem = (Application.Current as App).Navigation.GetNavigationViewItems(this.GetType()).First(); beerItem.MenuItems.Add(new NavigationViewItem { Content = $"Round {beerItem.MenuItems.Count + 1}", Tag = typeof(BeerDetailsPage).FullName }); beerItem.IsExpanded = true;}
Here’s such a detail page:
It has to buttons to iterate back and forth through its list of siblings. The ‘previous’ button navigates backwards through the list of all BeerDetailPage items in the menu. These may be spread over multiple parent items. The ‘next’ button shows how to limit the navigation to the parent of the detail page. This algorithm is a bit more cumbersome since child menu items don’t have a reference to their parent:
private void Button_Click(object sender, RoutedEventArgs e){ // Navigation through colleagues var navigation = (Application.Current as App).Navigation; var item = navigation.GetCurrentNavigationViewItem(); var siblings = navigation.GetNavigationViewItems(this.GetType()); var index = siblings.IndexOf(item); if (index > 0) { navigation.SetCurrentNavigationViewItem(siblings[index - 1]); }}private void Button_Click_1(object sender, RoutedEventArgs e){ // Navigation within parent var navigation = (Application.Current as App).Navigation; var item = navigation.GetCurrentNavigationViewItem(); var mainItems = navigation.GetNavigationViewItems(); foreach (var mainItem in mainItems) { // Find the parent if (mainItem.MenuItems.Contains(item)) { var siblings = mainItem.MenuItems; var index = siblings.IndexOf(item); if (index < siblings.Count - 1) { navigation.SetCurrentNavigationViewItem((NavigationViewItem)siblings[index + 1]); } } }}
There is definitely room for extra helper methods and a higher abstraction level, but in just a handful lines of C# we created the core of a service that covers most of the navigation requirements for an WinUI 3 app that uses a NavigationView control.
Our sample app lives here on GitHub.
Enjoy!
Advertisement
FAQs
How do I navigate in UWP? ›
- First of all open visual studio.
- Create new project.
- Select universal>installed>blank App(windows platform).
- Name it as u want and then click OK.
- Go to Mainpage. ...
- (by selecting appbar button u can go to properties>brushes.
- You can colour if u want)
WinUI is a user interface layer that contains modern controls and styles for building Windows apps. As the native UI layer in Windows it embodies Fluent Design, giving each Windows app the polished feel that customers expect. WinUI 2 is a library of controls and styles currently available for use in any UWP app.
How do I get started with WinUI 3? ›In Visual Studio, select File > New > Project. In the New Project dialog's drop-down filters, select C#/C++, Windows, and WinUI, respectively. Select the Blank App, Packaged (WinUI 3 in Desktop) project template, and click Next. That template creates a desktop app with a WinUI 3-based user interface.
What is the difference between UWP and WinUI? ›Unlike UWP, which is only compatible with Windows 10 devices, WinUI is backward-compatible with earlier versions of Windows 10 and 11. For example, with WinUI 3, you can build and ship apps with new features without waiting for users to run the latest Windows update.
What is navigate () used for? ›useNavigation is a hook which gives access to navigation object. It's useful when you cannot pass the navigation prop into the component directly, or don't want to pass it in case of a deeply nested child. useNavigation() returns the navigation prop of the screen it's inside.
How does UWP app work? ›UWP apps work well with multiple types of input such as keyboard, mouse, touch, pen, and Xbox One controllers. If you need to further tailor your UI to a specific screen size or device, new layout panels and tooling help you design UI that can adapt to the different devices and form factors that your app may run on.
Is WinUI 3 ready for production? ›WinUI 3 can be used to build production-ready desktop/Win32 Windows apps.
Is Microsoft killing UWP? ›We will replace UWP with WinUI components in our v22. 1 release. UWP controls and demos will be removed from all distributions/installations. We will continue to provide support and minor updates to our UWP controls until December 2022.
Is WinUI 3 native? ›WinUI 3 is the native UI platform component that ships with the Windows App SDK (completely decoupled from Windows SDKs).
How do I create a UI for Windows application? ›- Click File > New, then select Dialog System Screenset on the New dialog box, and click OK.
- Click Yes on the message asking if you want to create a project.
- Select Windows GUI Project. ...
- Click Yes when asked whether or not you want to create this directory.
How do I package a Windows application? ›
- In Solution Explorer, open the solution for your application project.
- Right-click the project and choose Publish->Create App Packages (before Visual Studio 2019 version 16.3, the Publish menu is named Store).
XAML Islands is a layer of technology from Microsoft that ships as part of the Windows Community Toolkit. It allows your WPF, WinForms, and C++ Win32 applications to embed a UWP control in an interop layer on your application's windows.
How do I navigate a page in WPF? ›To package content for navigation, WPF provides the Page class. You can navigate from one Page to another declaratively, by using a Hyperlink, or programmatically, by using the NavigationService. WPF uses the journal to remember pages that have been navigated from and to navigate back to them.
How do I open UWP File Explorer? ›- Right-click the desktop and create a new shortcut.
- In “Create shortcut” wizard, enter the following part in the location box:
- explorer shell:AppsFolder\c5e2524a-ea46-4f67-841f-6a9465d9d515_cw5n1h2txyewy! ...
- Click Next and name the shortcut as UWP File Explorer.
...
Redirection to URL to another Webserver
- protected void Button4_Click(object sender, EventArgs e)
- {
- }