Share via


How to navigate using the back stack for Windows Phone 8

[ This article is for Windows Phone 8 developers. If you’re developing for Windows 10, see the latest documentation. ]

 

This topic demonstrates how to modify the navigation of your app by manipulating its navigation history, called the back stack. You can use the NavigationService API to inspect and to work with the navigation history. The properties and methods of the NavigationService class are used in this topic to inspect the back stack, remove entries, and then observe the effect that these changes have on the navigation through the app.

Note

The terms navigation history and back stack are used interchangeably throughout this topic to refer to the navigation history that is exposed by the NavigationService..::.BackStack property.

A completed sample of an app that demonstrates this functionality is available for download. For more information, see Navigate Using the Back Stack for Windows Phone. This downloadable sample targets Windows Phone OS 7.1. The code in this topic has been adapted to target Windows Phone 8.

This topic contains the following sections.

 

Introducing the navigation history or back stack

The navigation history of an app is represented as a last-in, first-out structure called a stack. This is also referred to here as the back stack, since it contains the set of pages in a stack structure that represents the back navigation of your app.

Think of this stack as a stack of plates. The last plate that you add to the stack is the first plate that you can remove. The newest item is added to the top of this stack. This is referred to as a push operation. You can retrieve something from a stack only by removing items one at a time from the top of the stack. Removing the top item from the stack is referred to as a pop operation. The concept of a stack is shown in the following figure.

When a page in your app calls Navigate, the current page is put onto the back stack and a new instance of the destination page is created and displayed. As you navigate through pages in an app, entries are added to this stack.

When a page calls GoBack, or when the user presses the phone’s Back button, the current page is abandoned and the page at the top of the stack is popped off the back stack and displayed. This back navigation continues until there are no more entries in the stack. At this point, a tap of the phone’s Back button will exit the app.

Most apps never have to manipulate the back stack, and function perfectly well with the default navigation. Other apps have to adjust the navigation history to provide the best user experience. For example, your app may have a login page. After the user logs in, you may not want the user to be able to navigate back to the login page.

This topic demonstrates how to use the BackStack property and the RemoveBackEntry method to manipulate the navigation history.

Creating a sample app to visualize the back stack

This section describes how to visualize the navigation history or back stack in an app so that you can inspect it easily while the app is running. The sample app consists of multiple pages. As you navigate from one page to the next, you can see what entries are on the back stack. You can also easily remove entries, and see these updates reflected in the back stack. In this app, the back stack is displayed as a list as shown in the following figure.

The grey area in the preceding figure is the visualization of the back stack in the sample app. As you navigate through the app, the list is populated with the entries from the navigation history. You can use the Pop Last and Pop To Selected buttons to change the navigation history.

The gray list area is not coded on each individual page. Instead, the list is added in a single location to the app’s RootFrame. The RootFrame object is the PhoneApplicationFrame associated with the app. Each app has one RootFrame. Each page of the app, or instance of PhoneApplicationPage, is set as the Content of this frame by the navigation framework when the user navigates to that page. The default template for the RootFrame object that you get when you create a new Windows Phone app displays the app pages, and other elements such as the system tray and app bar for the app. In this example, you create a template that shows each page, but also leaves room to display the back stack as a list at the bottom of the screen. As you navigate from page to page, this list is updated to reflect the current state of the navigation history or back stack of the app.

For more information about the anatomy of a Windows Phone app, see In-app navigation for Windows Phone 8.

Creating the sample app

To visualize the back stack

  1. In Visual Studio, create a new project by selecting the File | New | Project menu command.

  2. The New Project window is displayed. Expand the Visual C# templates, and then select the Windows Phone templates.

  3. Select the **Windows Phone App ** template. Fill in the Name with a name of your choice.

  4. Click OK. The Windows Phone platform selection dialog box appears. Select a target version, or accept the default.

  5. Click OK. A new project is created, and MainPage.xaml is opened in the Visual Studio designer window.

  6. The next step is to change the template used by the RootFrame of the app to visualize the back stack. This is done by placing a ListBox in a custom ControlTemplate for the RootFrame. In the App.xaml file, add the following markup to the Application.Resources entry.

    The template is made up of a grid with two rows. The ContentPresenter is in the first row of the grid. This is where each page’s content will be displayed. Another grid is added to the second row of this grid to contain the UI for the back stack. This UI consists of a ListBox to show each entry in the back stack as well as some buttons to manipulate the back stack.

    <Application.Resources>
            <ControlTemplate x:Name="NewFrameTemplate">
                <Grid x:Name="ClientArea">
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>
                    <ContentPresenter Grid.Row="0"/>
                    <Border Grid.Row="1" BorderBrush="{StaticResource PhoneForegroundBrush}" BorderThickness="{StaticResource PhoneBorderThickness}" Height="300">
                        <Grid x:Name="ContentPanel" Background="{StaticResource PhoneSemitransparentBrush}">
                            <Grid.RowDefinitions>
                                <RowDefinition Height="Auto"/>
                                <RowDefinition />
                                <RowDefinition Height="Auto"/>
                            </Grid.RowDefinitions>
                            <TextBlock  Grid.Row="0" x:Name="CurrentPage" Style="{StaticResource PhoneTextSubtleStyle}" HorizontalAlignment="Center"/>
                            <ListBox Grid.Row="1" ItemsSource="{Binding}" x:Name="HistoryList" 
                                    HorizontalAlignment="Center" Height="300">
                                <ListBox.ItemTemplate>
                                    <DataTemplate>
                                        <Border BorderBrush="{StaticResource PhoneForegroundBrush}" BorderThickness="{StaticResource PhoneBorderThickness}" Width="300" Margin="5" 
                                                Background="DarkGray" HorizontalAlignment="Center">
                                            <TextBlock Text="{Binding}"/>
                                        </Border>
                                    </DataTemplate>
                                </ListBox.ItemTemplate>
                            </ListBox>
                            <StackPanel Grid.Row="2" Orientation="Horizontal" HorizontalAlignment="Center">
                                <Button Content="Pop Last" x:Name="btnPopLast" IsEnabled="false"/>
                                <Button Content="Pop To Selected" x:Name="btnPopToSelected" IsEnabled="false"/>
                            </StackPanel>
                        </Grid>
                    </Border>
    
                </Grid>
            </ControlTemplate>
        </Application.Resources>
    
  7. In the App.xaml.cs code-behind file, add the following using statement.

    using System.Windows.Controls;
    

    Then, add the following declarations to the top of the App class. These will be used to reference the UI elements that were created from the custom template.

    // UI controls on the RootFrame template.
    ListBox historyListBox;            // ListBox for listing the navigation history
    Button popLastButton;              // Button to pop the newest entry from the back stack
    Button popToSelectedButton;        // Button to pop all entries in the back stack up to the selected entry
    TextBlock currentPageTextBlock;    // TextBlock to display the current page the user is on
    
  8. In the App.xaml.cs code-behind file, add the following using statement.

    using System.Windows.Media;
    

    Then, add the following lines of code in the constructor, directly after the call to InitializePhoneApplication. First, the template for the RootFrame is set to NewFrameTemplate, which is the name of the template you defined in Step 6. The event handlers for the buttons on the template are also hooked up here. Finally, a delegate is defined for the Navigated event of the RootFrame to update the history.

    Note that the event handlers have not yet been created.

    // Set the template for the RootFrame to the new template you created in the Application.Resources in App.xaml
    RootFrame.Template = Resources["NewFrameTemplate"] as ControlTemplate;
    RootFrame.ApplyTemplate();
    
    popToSelectedButton = (VisualTreeHelper.GetChild(RootFrame, 0) as FrameworkElement).FindName("btnPopToSelected") as Button;
    popToSelectedButton.Click += new RoutedEventHandler(PopToSelectedButton_Click);
    
    popLastButton = (VisualTreeHelper.GetChild(RootFrame, 0) as FrameworkElement).FindName("btnPopLast") as Button;
    popLastButton.Click += new RoutedEventHandler(PopLastButton_Click);
    
    currentPageTextBlock = (VisualTreeHelper.GetChild(RootFrame, 0) as FrameworkElement).FindName("CurrentPage") as TextBlock;
    
    historyListBox = (VisualTreeHelper.GetChild(RootFrame, 0) as FrameworkElement).FindName("HistoryList") as ListBox;
    historyListBox.SelectionChanged += new SelectionChangedEventHandler(HistoryList_SelectionChanged);
    
    // Update the navigation history listbox whenever a navigation happens in the application
    RootFrame.Navigated += delegate { RootFrame.Dispatcher.BeginInvoke(delegate { UpdateHistory(); }); };
    
  9. Add the following methods to the App.xaml.cs code-behind file. UpdateHistory refreshes the UI for the navigation back stack. This is called when the Navigated event fires on the RootFrame, which occurs whenever any navigation happens in the app. The method iterates over all entries in the BackStack property and adds them to the ListBox. It also displays the URI of the current page. If there are entries in the navigation back stack, the Pop Last button is enabled. HistoryList_SelectionChanged enables or disables the Pop To Selected button depending on whether an item in the navigation history list was selected.

    /// <summary>
    /// Use the BackStack property to refresh the navigation history list box with the latest history.
    /// </summary>
    void UpdateHistory()
    {
       historyListBox.Items.Clear();
       int i = 0;
    
       foreach (JournalEntry journalEntry in RootFrame.BackStack)
       {
          historyListBox.Items.Insert(i, journalEntry.Source);
          i++;
       }
       currentPageTextBlock.Text = "[" + RootFrame.Source + "]";
       if (popLastButton != null)
       {
           popLastButton.IsEnabled = (historyListBox.Items.Count > 0);
       }
    }
    
    /// <summary>
    /// Handle the SelectionChanged event for navigation history list.
    /// </summary>
    private void HistoryList_SelectionChanged(object sender, SelectionChangedEventArgs e)
    {
       if (historyListBox != null && popToSelectedButton != null)
       {
           popToSelectedButton.IsEnabled = (historyListBox.SelectedItems.Count > 0) ? true : false;
       }
    }
    
  10. Add the following method to the App.xaml.cs code-behind file. This handles the click event on the Pop Last button. When the user taps this button, the last entry added to the back stack is removed. This method uses the RemoveBackEntry method on the RootFrame. After the entry has been removed, the list of back stack entries is refreshed by calling UpdateHistory.

    /// <summary>
    /// Remove the last entry from the back stack.
    /// </summary>
    private void PopLastButton_Click(object sender, RoutedEventArgs e)
    {
    
        RootFrame.RemoveBackEntry();
    
        // Refresh the history list since the back stack has been modified.
        UpdateHistory();
    }
    
  11. Add the following method to the App.xaml.cs code-behind file. If the user selects an item on the back stack and taps Pop To Selected, all entries up to the selected entry are removed from the back stack. The RemoveBackEntry method on the RootFrame is used to remove each entry from the back stack. After the entries have been removed, the back stack UI is refreshed by calling UpdateHistory.

    /// <summary>
    /// Remove all entries from the back stack up to the selected item, but not including it.
    /// </summary>
    private void PopToSelectedButton_Click(object sender, RoutedEventArgs e)
    {
        // Make sure something has been selected.
        if (historyListBox != null && historyListBox.SelectedIndex >= 0)
        {
            for (int i = 0; i < historyListBox.SelectedIndex; i++)
            {
                RootFrame.RemoveBackEntry();
            }
            // Refresh the history list since the back stack has been modified.
            UpdateHistory();
        }
     }
    

This section described how to add a custom template for the RootFrame of the app so that you can inspect and modify the back stack as you navigate through the app. To demonstrate this functionality, you have to add multiple pages to the app. This is described in the next section.

Adding pages to the app

To demonstrate navigation history inspection and manipulation, you have to add multiple pages to the app. This section describes how to add these pages to the app. The example has four pages: MainPage.xaml, Page1.xaml, Page2.xaml, and Page3.xaml. Each page is structured the same and uses the same UI. As a result, the following steps will be repeated for each page. In this example, no attempt has been made to reuse code through helper methods or by wrapping the UI in a UserControl.

To add pages to the app

  1. Add a new page to the project by selecting the Project | Add New Item menu command. The Add New Item window will be displayed. Select Windows Phone Portrait Page in the list of items to add and type Page1.xaml in the Name field. Click Add to add the new page to your project. A new page named Page1.xaml is now added to the project.

  2. The first thing to do is to define the page UI. In Page1.xaml, replace the grid named ContentPanel with the following code. This creates a CheckBox used to toggle whether the page is pinned to the Start screen and a Button to navigate to the next page in the app, if a next page exists. Pinning the page to the Start screen of the phone is a very interesting case and is demonstrated here to show the state of the back stack when you click on the page’s Tile on the Start screen to launch the app. The event handlers shown in the following code are described in subsequent steps.

    <!--ContentPanel - place additional content here-->
    <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
        <CheckBox x:Name="PinToStartCheckBox" Content="Pin To Start" IsChecked="False" HorizontalAlignment="Center" 
    VerticalAlignment="Center" Click="PinToStartCheckBox_Click"/>
        <Button x:Name="btnNext" Content="Next Page"  Height="80" VerticalAlignment="Bottom" 
    Click="btnNext_Click"/>
    </Grid>
    
  3. Assign names to the 2 XAML TextBlock controls so you can set their values programmatically.

            <StackPanel Grid.Row="0" Margin="12,17,0,28">
                <TextBlock x:Name="AppName" Text="MY APPLICATION" Style="{StaticResource PhoneTextNormalStyle}"/>
                <TextBlock x:Name="PageTitle" Text="page name" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
            </StackPanel>
    
  4. In the Page1.xaml.cs code-behind file, add the following variable declaration to the top of the class.

    // The URI string of the next page to navigate to from this page.
    // String.Empty here means that there is no next page.
    private string nextPage;
    
  5. Add the following lines of code after InitializeComponent() in the class constructor.

Note

The steps in this procedure are repeated for several additional pages. For each additional page that you add, remember to update the page title and the value of the nextPage variable in this code.

``` csharp
// Set the application title - use the same application title on each page.
AppName.Text = "SDK BACKSTACK SAMPLE";

// Set a unique page title. In this example, you will use "page 1", "page 2", and so on.
PageTitle.Text = "page 1";

// Set the URI string of the next page, or String.Empty if there is no next page.
nextPage = "/Page2.xaml";
```
  1. Add the following method to override the OnNavigatedTo event handler. Here the Next Page button is shown if this page has the nextPage variable set. The Pin To Start check box is set depending on whether a Tile for this page exists.

    protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e)
    {
       base.OnNavigatedTo(e);
    
       // Show the Next button, if you have defined a next page.
       btnNext.Visibility = (String.IsNullOrWhiteSpace(nextPage)) ? Visibility.Collapsed : Visibility.Visible;
    
       if (ShellTile.ActiveTiles.FirstOrDefault(o => o.NavigationUri.ToString().Contains(NavigationService.Source.ToString())) == null)
          PinToStartCheckBox.IsChecked = false;
       else
          PinToStartCheckBox.IsChecked = true;
    }
    
  2. Add the following method to handle the Click event for the Next Page button on the page. If the nextpage variable has been defined for this page, the Navigate method is called to navigate to that page.

    /// <summary>
    /// Navigate to the next page.
    /// </summary>
    private void btnNext_Click(object sender, RoutedEventArgs e)
    {
         // Make sure to attempt navigation only if you have defined a next page.
         if (!String.IsNullOrWhiteSpace(nextPage))
         {
            this.NavigationService.Navigate(new Uri(nextPage, UriKind.Relative));
         }
    }
    
  3. Add the following method to handle the Click event for the Pin To Start check box on the page. This acts like a toggle. If a Tile for this page already exists on the Start screen, it is removed. If a Tile for this page does not exist on the Start screen, one is added. For more information about Tiles, see Tiles for Windows Phone 8.

    /// <summary>
    /// Toggle pinning a Tile for this page on the Start screen.
    /// </summary>
    private void PinToStartCheckBox_Click(object sender, RoutedEventArgs e)
    {
       // Try to find a Tile that has this page's URI.
       ShellTile tile = ShellTile.ActiveTiles.FirstOrDefault(o => o.NavigationUri.ToString().Contains(NavigationService.Source.ToString()));
    
       if (tile == null)
       {
          // No Tile was found, so add one for this page.
          StandardTileData tileData = new StandardTileData { Title = PageTitle.Text };
          ShellTile.Create(new Uri(NavigationService.Source.ToString(), UriKind.Relative), tileData);
       }
       else
       {
          // A Tile was found, so remove it.
          tile.Delete();
       }
    }
    

Note

If your app lets users pin pages, consider whether a home button is required to let the user get back to the root of the app quickly. A home button navigates to the home page of the app and then clears the entire navigation back stack. Examine each navigation scenario and determine whether this functionality is needed. If the pinned page is self-contained, such as a page of contact information, it’s likely that the user will want to tap on the page, look at the information, and then back out of the app. Backing out of the app in this case can be done by using the hardware Back button. If the pinned page is an entry point into the app from which the user will navigate deeply, then a quick way to get back to the root of the app may be required. For example, if the pinned page is a shopping cart, the user may want to complete the purchases in the shopping cart and then start shopping again. In this case, giving the user a home button improves the user experience by reducing the number of taps they need to get back to the start of the app.

  1. The previous steps describe how to add a page and update it for use in this app. To complete the app, repeat these steps for MainPage, Page2, and Page3 as follows.

Page name

New page?

Changes for step 5

MainPage.xaml

No. This page was created when you created the app. Repeat steps 2 through 8 for this page.

PageTitle.Text = “main”;

nextPage = “/Page1.xaml”;

Page2.xaml

Yes. Repeat steps 1 through 8 for this page.

PageTitle.Text = “page 2”;

nextPage = “/Page3.xaml”;

Page3.xaml

Yes. Repeat steps 1 through 8 for this page.

PageTitle.Text = “page 3”;

nextPage = String.Empty

Specify String.Empty for the next page because page 3 is the last page in the app. You can’t navigate forward from page 3.

When you have completed this section, your solution in the Solution Explorer is shown in the following figure. The app has four pages, and the forward navigation through these pages is as follows: MainPage.xaml -> Page1.xaml -> Page2.xaml -> Page3.xaml.

Testing the app

This section describes how to run the app that was produced in this topic.

To test the app

  1. Run the app by selecting the Debug | Start Debugging menu command.

  2. The app launches, and the MainPage.xaml page is displayed. This is shown in the following figure. The bottom half of the screen, displays the back stack visualization. There is nothing in the navigation history at this point, so the list is empty. The current page is shown as [/MainPage.xaml]

  3. Tap the Next Page button. Observe that the page changes to page 1 and the back stack list now contains /MainPage.xaml.

  4. Tap the Next Page button again. The current page is page 2. The back stack now contains two entries: /Page1.xaml and /MainPage.xaml. As a stack, it shows the newest entry at the top and the oldest entry at the bottom. This is illustrated in the following figure.

  5. Tap the Pop Last button. The /Page1.xaml entry is removed from the back stack list.

  6. Tap the hardware Back button on your device. The page main page is displayed and the back stack is empty. This happened because you removed /Page1.xaml from the back stack in the previous step, thus changing the back navigation from Page 2 -> Page1 -> MainPage to Page2 -> MainPage.

  7. Tap the Next Page button on main page. The page page 1 is displayed. The back stack has one entry, /MainPage.xaml.

  8. Tap the Next Page button on page 1. The page page 2 is now displayed. The back stack has two entries, /Page1.xaml and /MainPage.xaml.

  9. Tap Pin To Start on page 2.

  10. A Tile named page 2 is created on the Start screen of the device.

  11. Tap the Tile you created in the preceding step. The app is launched, and page 2 is displayed. Notice also that the back stack is empty. If you tap the Back button on the device, the app terminates. An example of this state is shown in the following figure. page 2 was pinned to the Start screen on the device. The Tile was then tapped, the app launched, and Page2 is displayed. The back stack shown in this figure is empty.

See Also

Reference

NavigationService

BackStack

Other Resources

Navigation Backstack Sample

Frame, page, and navigation features for Windows Phone 8