Implement navigation between two pages

Learn how to use a frame and pages to enable basic navigation in your app.

Important APIs:Windows.UI.Xaml.Controls.Frame class, Windows.UI.Xaml.Controls.Page class, Windows.UI.Xaml.Navigation namespace

1. Create a blank app

  1. On the Microsoft Visual Studio menu, choose File > New Project.
  2. In the left pane of the New Project dialog box, choose the Visual C# -> Windows -> Universal or the Visual C++ -> Windows -> Universal node.
  3. In the center pane, choose Blank App.
  4. In the Name box, enter NavApp1, and then choose the OK button.

    The solution is created and the project files appear in Solution Explorer.

  5. To run the program, choose Debug > Start Debugging from the menu, or press F5.

    A blank page is displayed.

  6. Press Shift+F5 to stop debugging and return to Visual Studio.

2. Add basic pages

Next, add two content pages to the project.

Do the following steps two times to add two pages to navigate between.

  1. In Solution Explorer, right-click the BlankApp project node to open the shortcut menu.
  2. Choose Add > New Item from the shortcut menu.
  3. In the Add New Item dialog box, choose Blank Page in the middle pane.
  4. In the Name box, enter Page1 (or Page2) and press the Add button.

These files should now be listed as part of your NavApp1 project.

C# C++
  • Page1.xaml
  • Page1.xaml.cs
  • Page2.xaml
  • Page2.xaml.cs
  • Page1.xaml
  • Page1.xaml.cpp
  • Page1.xaml.h
  • Page2.xaml
  • Page2.xaml.cpp
  • Page2.xaml.h

Add the following content to the UI of Page1.xaml.

  • Add a TextBlock element named pageTitle as a child element of the root Grid. Change the Text property to Page 1.
<TextBlock x:Name="pageTitle" Text="Page 1" />
<HyperlinkButton Content="Click to go to page 2"
                 Click="HyperlinkButton_Click"
                 HorizontalAlignment="Center"/>

Add the following code to the Page1 class in the Page1.xaml code-behind file to handle the Click event of the HyperlinkButton you added previously. Here, we navigate to Page2.xaml.

private void HyperlinkButton_Click(object sender, RoutedEventArgs e)
{
    this.Frame.Navigate(typeof(Page2));
}
void Page1::HyperlinkButton_Click(Platform::Object^ sender, RoutedEventArgs^ e)
{
    this->Frame->Navigate(Windows::UI::Xaml::Interop::TypeName(Page2::typeid));
}

Make the following changes to the UI of Page2.xaml.

  • Add a TextBlock element named pageTitle as a child element of the root Grid. Change the value of the Text property to Page 2.
<TextBlock x:Name="pageTitle" Text="Page 2" />
<HyperlinkButton Content="Click to go to page 1" 
                 Click="HyperlinkButton_Click"
                 HorizontalAlignment="Center"/>

Add the following code to the Page2 class in the Page2.xaml code-behind file to handle the Click event of the HyperlinkButton you added previously. Here, we navigate to Page1.xaml.

Note

For C++ projects, you must add a #include directive in the header file of each page that references another page. For the inter-page navigation example presented here, page1.xaml.h file contains #include "Page2.xaml.h", in turn, page2.xaml.h contains #include "Page1.xaml.h".

private void HyperlinkButton_Click(object sender, RoutedEventArgs e)
{
    this.Frame.Navigate(typeof(Page1));
}
void Page2::HyperlinkButton_Click(Platform::Object^ sender, RoutedEventArgs^ e)
{
    this->Frame->Navigate(Windows::UI::Xaml::Interop::TypeName(Page1::typeid));
}

Now that we've prepared the content pages, we need to make Page1.xaml display when the app starts.

Open the app.xaml code-behind file and change the OnLaunched handler.

Here, we specify Page1 in the call to Frame.Navigate instead of MainPage.

protected override void OnLaunched(LaunchActivatedEventArgs e)
{
    Frame rootFrame = Window.Current.Content as Frame;

    // Do not repeat app initialization when the Window already has content,
    // just ensure that the window is active
    if (rootFrame == null)
    {
        // Create a Frame to act as the navigation context and navigate to the first page
        rootFrame = new Frame();

        rootFrame.NavigationFailed += OnNavigationFailed;

        if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
        {
            //TODO: Load state from previously suspended application
        }

        // Place the frame in the current Window
        Window.Current.Content = rootFrame;
    }

    if (rootFrame.Content == null)
    {
        // When the navigation stack isn't restored navigate to the first page,
        // configuring the new page by passing required information as a navigation
        // parameter
        rootFrame.Navigate(typeof(Page1), e.Arguments);
    }
    // Ensure the current window is active
    Window.Current.Activate();
}
void App::OnLaunched(Windows::ApplicationModel::Activation::LaunchActivatedEventArgs^ e)
{
    auto rootFrame = dynamic_cast<Frame^>(Window::Current->Content);

    // Do not repeat app initialization when the Window already has content,
    // just ensure that the window is active
    if (rootFrame == nullptr)
    {
        // Create a Frame to act as the navigation context and associate it with
        // a SuspensionManager key
        rootFrame = ref new Frame();

        rootFrame->NavigationFailed += 
            ref new Windows::UI::Xaml::Navigation::NavigationFailedEventHandler(
                this, &App::OnNavigationFailed);

        if (e->PreviousExecutionState == ApplicationExecutionState::Terminated)
        {
            // TODO: Load state from previously suspended application
        }

        // Place the frame in the current Window
        Window::Current->Content = rootFrame;
    }

    if (rootFrame->Content == nullptr)
    {
        // When the navigation stack isn't restored navigate to the first page,
        // configuring the new page by passing required information as a navigation
        // parameter
        rootFrame->Navigate(Windows::UI::Xaml::Interop::TypeName(Page1::typeid), e->Arguments);
    }

    // Ensure the current window is active
    Window::Current->Activate();
}

Note The code here uses the return value of Navigate to throw an app exception if the navigation to the app's initial window frame fails. When Navigate returns true, the navigation happens.

Now, build and run the app. Click the link that says "Click to go to page 2". The second page that says "Page 2" at the top should be loaded and displayed in the frame.

About the Frame and Page classes

Before we add more functionality to our app, let's look at how the pages we added provide navigation support for the app.

First, a Frame (rootFrame) is created for the app in the App.OnLaunched method of the App.xaml code-behind file. The Navigate method is used to display content in this Frame.

Note
The Frame class supports various navigation methods such as Navigate, GoBack, and GoForward, and properties such as BackStack, ForwardStack, and BackStackDepth.

In our example, Page1 is passed to the Navigate method. This method sets the content of the app's current window to the Frame and loads the content of the page you specify into the Frame (Page1.xaml in our example, or MainPage.xaml, by default).

Page1 is a subclass of the Page class. The Page class has a read-only Frame property that gets the Frame containing the Page. When the Click event handler of the HyperlinkButton callsFrame.Navigate(typeof(Page2)), the Frame in the app's window displays the content of Page2.xaml.

Whenever a page is loaded into the frame, that page is added as a PageStackEntry to the BackStack or ForwardStack of the Frame.

3. Pass information between pages

Our app navigates between two pages, but it really doesn't do anything interesting yet. Often, when an app has multiple pages, the pages need to share information. Let's pass some information from the first page to the second page.

In Page1.xaml, replace the the HyperlinkButton you added earlier with the following StackPanel.

Here, we add a TextBlock label and a TextBox (name) for entering a text string.

<StackPanel>
    <TextBlock HorizontalAlignment="Center" Text="Enter your name"/>
    <TextBox HorizontalAlignment="Center" Width="200" Name="name"/>
    <HyperlinkButton Content="Click to go to page 2" 
                     Click="HyperlinkButton_Click"
                     HorizontalAlignment="Center"/>
</StackPanel>

In the HyperlinkButton_Click event handler of the Page1.xaml code-behind file, add a parameter referencing the Text property of the name TextBox to the Navigate method.

private void HyperlinkButton_Click(object sender, RoutedEventArgs e)
{
    this.Frame.Navigate(typeof(Page2), name.Text);
}
void Page1::HyperlinkButton_Click(Platform::Object^ sender, RoutedEventArgs^ e)
{
    this->Frame->Navigate(Windows::UI::Xaml::Interop::TypeName(Page2::typeid), name->Text);
}

In Page2.xaml, replace the the HyperlinkButton you added earlier with the following StackPanel.

Here, we add a TextBlock for displaying a text string passed from Page1.

<StackPanel>
    <TextBlock HorizontalAlignment="Center" Name="greeting"/>
    <HyperlinkButton Content="Click to go to page 1" 
                     Click="HyperlinkButton_Click"
                     HorizontalAlignment="Center"/>
</StackPanel>

In the Page2.xaml code-behind file, override the OnNavigatedTo method with the following:

protected override void OnNavigatedTo(NavigationEventArgs e)
{
    if (e.Parameter is string && !string.IsNullOrWhiteSpace((string)e.Parameter))
    {
        greeting.Text = $"Hi, {e.Parameter.ToString()}";
    }
    else
    {
        greeting.Text = "Hi!";
    }
    base.OnNavigatedTo(e);
}
void Page2::OnNavigatedTo(NavigationEventArgs^ e)
{
    if (dynamic_cast<Platform::String^>(e->Parameter) != nullptr)
    {
        greeting->Text = "Hi," + e->Parameter->ToString();
    }
    else
    {
        greeting->Text = "Hi!";
    }
    ::Windows::UI::Xaml::Controls::Page::OnNavigatedTo(e);
}

Run the app, type your name in the text box, and then click the link that says Click to go to page 2. When you called this.Frame.Navigate(typeof(Page2), name.Text) in the Click event of the HyperlinkButton, the name.Text property was passed to Page2 and the value from the event data is used for the message displayed on the page.

4. Cache a page

Page content and state is not cached by default, you must enable it in each page of your app.

In our basic peer-to-peer example, there is no back button (we demonstrate back navigation in Back button navigation), but if you did click a back button on Page2, the TextBox (and any other field) on Page1 would be set to its default state. One way to work around this is to use the NavigationCacheMode property to specify that a page be added to the frame's page cache.

In the constructor of Page1, set NavigationCacheMode to Enabled. This retains all content and state values for the page until the page cache for the frame is exceeded.

Set NavigationCacheMode to Required if you want to ignore cache size limits for the frame. However, cache size limits might be crucial, depending on the memory limits of a device.

Note

The CacheSize property specifies the number of pages in the navigation history that can be cached for the frame.

public Page1()
{
    this.InitializeComponent();
    this.NavigationCacheMode = Windows.UI.Xaml.Navigation.NavigationCacheMode.Enabled;
}
Page1::Page1()
{
    this->InitializeComponent();
    this->NavigationCacheMode = Windows::UI::Xaml::Navigation::NavigationCacheMode::Enabled;
}