WinUI 3.0 Desktop - C# Page Navigation
Asked Answered
C

3

8

I am trying to do a simple page navigation but I am unable to find any documentation on how to do so in WinUI 3.0.

Currently, when I create a Blank App using WinUI 3.0, I get the following code created in App.xaml.cs

    protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
    {
        m_window = new MainWindow();
        m_window.Activate();
    }

    private Window m_window;

While in many other examples I've found on the web, a root frame is defined in the OnLaunched event above.

How am I to define MainWindow.xaml or App.xaml such that I can get a frame where I can freely switch between Page1.xaml and Page2.xaml?

Edit: I've now found out that I can retrieve the frame by calling:

    protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
    {
        m_window = new MainWindow();
        Frame rootFrame = m_window.Content as Frame;
        m_window.Activate();
        rootFrame.Navigate(typeof(UI.MainMenu));
    }

But Navigate fails with a System.NullReferenceException: 'Object reference not set to an instance of an object.' error. What am I doing wrong :S?

Cliquish answered 17/6, 2021 at 13:5 Comment(2)
can you post a stack trace to the exception?Fabiolafabiolas
System.NullReferenceException HResult=0x80004003 Message=Object reference not set to an instance of an object. Source=StackOverflow StackTrace: at StackOverflow.App.OnLaunched(LaunchActivatedEventArgs args) in S:\dev\StackOverflow\StackOverflow\StackOverflow\App.xaml.cs:line 48Cliquish
A
6

Probably the best way is to create a Frame object and assign it to m_window, rather than have a dedicated XAML window. I think you can simply do this:

this.m_window = new Window();
this.m_window.Content = rootFrame = new Frame(); ;
this.m_window.Activate();
rootFrame.Navigate(typeof(MainPage));

You'll need to rename the MainWindow to MainPage and change it's root element type to Page.

In your App class, you can expose a public Navigate method that delegates to the rootFrame's Navigate. In so doing, you will be able to use App.Current to get the app instance from anywhere in your app, thus causing your app's root frame to navigate forward in the back stack.

Alternatively, make a Prism-like navigation service to provide navigation services throughout the app.

Ambry answered 29/11, 2021 at 19:26 Comment(0)
C
4

So, I managed to find a way to do this.

First, I created a window that I will be referring to as the NavigationRootWindow.

<Window
x:Class="StackOverflow.UI.NavigationRootWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:StackOverflow.UI"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d">

<Grid>
    <Frame x:Name="rootFrame"/>
</Grid>

In here, I added a frame that I named rootFrame.

Now, in the App I define the onLaunched function as is autogenerated, but I make sure that the window that is added, is the root window:

    protected override void OnLaunched(Microsoft.UI.Xaml.LaunchActivatedEventArgs args)
    {
        m_window = new UI.NavigationRootWindow();
        m_window.Activate();
    }

    private Window m_window;

Now, in the cs file of the NavigationRootWindow, I can navigate to the page file that I want:

public sealed partial class NavigationRootWindow : Window
{
    public NavigationRootWindow()
    {
        this.InitializeComponent();

        rootFrame.Navigate(typeof(MainMenu));
    }
}

Where MainMenu is a page element :) Now, in a page element, you can just call this.Frame to get the current frame - which allows you to navigate to another page and even back and forth. An example:

    private void ConnectButton_OnClick(object sender, RoutedEventArgs e)
    {
        this.Frame.Navigate(typeof(SecondMenu));
    }

I'm not certain that this is best practice, but it indeed works :D

Cliquish answered 21/6, 2021 at 19:36 Comment(1)
Thank you for your answer! Do you also know how to navigate to an existing page instance? Calling the Frame.Navigate method like in your example always creates a new instance of the Page. As a consequence in the "OnNavigatedTo" event of the page e.NavigationMode is always NavigationMode.New instead of NavigationMode.Refresh. It seems that in the "old" .NET framework there was a class "NavigationWindow" which served this purpose: learn.microsoft.com/en-us/dotnet/desktop/wpf/app-development/…Octahedron
I
1

Just beginning with WinUI 3 myself, and here is the simplest way I found to navigate between pages.

In your apps main Window page add the following between the Window tags.

<Grid  x:Name="Root">
    <NavigationView x:Name="NavigationView"
                    Loaded="NavigationView_Loaded"
                    SelectionChanged="NavigationView_SelectionChanged"                                          
                    Header="Basic Page Navigation"                    
                    Margin="0">
        <NavigationView.MenuItems>
            <NavigationViewItem Content="Home"
                                Tag="YourNameSpace.PageOne"
                                Icon="Download" >
            </NavigationViewItem>
            <NavigationViewItem Content="About"
                                Tag="YourNameSpace.PageTwo"
                                Icon="Refresh">
            </NavigationViewItem>
        </NavigationView.MenuItems>

        <Frame x:Name="ContentFrame"
               Padding="20">
        </Frame>

    </NavigationView>
</Grid>

This assumes you have two pages named PageOne and PageTwo. Replace "YourNameSpace" with the namespace of your pages.

In the code behind of the main Window add these two event handlers. The _Loaded handler is hard coded to load PageOne just to get something working.

private void NavigationView_Loaded(object sender, RoutedEventArgs e)
{
    ContentFrame.Navigate(typeof(PageOne));
}

private void NavigationView_SelectionChanged(NavigationView sender, NavigationViewSelectionChangedEventArgs args)
{
    NavigationViewItem nvi = args.SelectedItemContainer as NavigationViewItem;
    var tag = nvi.Tag as string;
    Type type = Type.GetType(tag);
    ContentFrame.Navigate(type);
}

That is as simple as I could make it.

Impulsive answered 27/5, 2023 at 19:57 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.