Partager via


Navigation Overview

Microsoft Silverlight will reach end of support after October 2021. Learn more.

This topic describes common navigation scenarios, and provides guidance about how to add navigation features to your application.

This topic contains the following sections:

  • Application Navigation

  • Web Browser-Integrated Navigation

  • External Navigation

  • Extending the Navigation System

Application Navigation

You implement application navigation in a Silverlight application by using the Frame and Page controls. Page controls represent discrete sections of content. The frame acts as a container for page controls, and facilitates navigation to pages. At any one time, the frame displays the content of a single page. You change the page that is displayed within the frame by navigating, either programmatically or through user action, to a new page.

You can design the root visual of your Silverlight application to contain a combination of navigable content plus permanent user-interface (UI) components, such as headers, footers, and navigation sidebars. When you create a new project by using the Silverlight Navigation Application template, the template generates a XAML file that contains permanent UI components and a frame for navigable content.

The following example shows a simple frame that exists inside of a UserControl control named MainPage. You could add other UI components to LayoutRoot before or after ContentFrame. The Source property is set to /Views/Home.xaml which means that by default the page displayed in the frame is the page located at /Views/Home.xaml.

<!-- NOTE: 
  By convention, the sdk prefix indicates a URI-based XAML namespace declaration 
  for Silverlight SDK client libraries. This namespace declaration is valid for 
  Silverlight 4 only. In Silverlight 3, you must use individual XAML namespace 
  declarations for each CLR assembly and namespace combination outside the scope 
  of the default Silverlight XAML namespace. For more information, see the help 
  topic "Prefixes and Mappings for Silverlight Libraries". 
-->
<UserControl x:Class="SilverlightApplication.MainPage"
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:sdk="https://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:d="https://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006" 
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480">
  <Grid x:Name="LayoutRoot">
      <sdk:Frame x:Name="ContentFrame" Source="/Views/Home.xaml">

      </sdk:Frame>
  </Grid>
</UserControl>

You can add new pages to your application by using the Add New Item dialog box and selecting Silverlight Page. The Silverlight Navigation Application template in Visual Studio creates a folder named Views that contains pages. You can add pages to this folder, or you can add pages anywhere in your application that is most appropriate.

Creating User-Friendly URIs

Within a frame, you can specify that a certain URI pattern maps to a particular page. URI mapping enables you to create URIs that are descriptive of the user action instead of a path to a file. For example, you can specify that any request for /Home is actually a request for the file at /Views/Home.xaml. Any request that does not match one of the defined patterns is handled as a regular URI request and is not mapped to a different page. The following table shows examples of URI mapping definitions and how example requests are resolved.

URI Mapping Definition

Example of Matching URI

Resolved URI

Uri = "/Home"

MappedUri = "/Views/Home.xaml"

/Home

/Views/Home.xaml

Uri = "/{page}"

MappedUri = "/Views/{page}Page.xaml"

/About

/Views/AboutPage.xaml

Uri = "/Product/{category}"

MappedUri = "/ContosoShop/Product.xaml?category={category}"

/Product/bikes

/ContosoShop/Product.xaml?category=bikes

Uri = "/{reporttype}/{month}/{format}"

(in XAML) MappedUri = "/Views/Reports/{reporttype}.xaml?time={month}&amp;show={format}"

(in Visual Basic or C#) MappedUri = "/Views/Reports/{reporttype}.xaml?time={month}&show={format}"

/Sales/June/Short

/Views/Reports/Sales.xaml?time=June&show=Short

You add URI mapping to a frame by defining an instance of the UriMapper class (or a custom class that derives from UriMapperBase class) and any number of UriMapping instances. The pattern you specify does not have to be the exact URI to match against the requested URI. The pattern can include placeholder segments in the URI that will match any value in that segment. You specify a placeholder segment by enclosing the name of the segment with curly braces ( { and } ). The placeholder segment acts as a variable when mapping to the URI. Any value that is not enclosed in curly braces represents a literal value that must be present for the URI to match the pattern. The following example shows URI patterns that include placeholder values.

<sdk:Frame 
       x:Name="ContentFrame" 
       Style="{StaticResource ContentFrameStyle}" 
       Source="/Home" 
       Navigated="ContentFrame_Navigated" 
       NavigationFailed="ContentFrame_NavigationFailed">
    <sdk:Frame.UriMapper>
        <sdk:UriMapper>
            <sdk:UriMapping 
                Uri="/ProductDetail/{productid}" 
                MappedUri="/Views/ProductDetail.xaml?ProductId={productid}"/>
            <sdk:UriMapping 
                Uri="/Reports/{type}/{selection}" 
                MappedUri="/Views/ReportsPage.xaml?type={type}&amp;selection={selection}"/>
            <sdk:UriMapping 
                Uri="/{pageName}" 
                MappedUri="/Views/{pageName}.xaml"/>
        </sdk:UriMapper>
    </sdk:Frame.UriMapper>
</sdk:Frame>

The URI request is mapped to the first pattern that matches the request. Therefore, add your URI mapping instances from most specific to most general. For example, the following definitions are correctly ordered from a specific literal value to a general placeholder value. A request for /SalesReport would map to /Views/Reports/Sales.xaml.

<sdk:Frame x:Name="ContentFrame" Source="/Home">
  <sdk:Frame.UriMapper>
    <sdk:UriMapper>
      <sdk:UriMapping Uri="/SalesReport" MappedUri="/Views/Reports/Sales.xaml"/>
      <sdk:UriMapping Uri="/{page}" MappedUri="/Views/{page}Page.xaml"/>
    </sdk:UriMapper>
  </sdk:Frame.UriMapper>
</sdk:Frame>

However, if you reverse the order of the URI mapping definitions, a request for /SalesReport would not map to /Views/Reports/Sales.xaml. Instead, the first definition /{page} would match every single segment request including /SalesReport. The request would map to /Views/SalesReportPage.xaml.

Facilitating Navigation to Pages

The Frame class provides methods and properties to navigate to pages. You set the Source property to the URI for the page to display, or you call the Navigate method and pass the URI for the page as a parameter. You can also use the GoBack and GoForward methods to navigate back or forward in the navigation history.

You can use the HyperlinkButton control to enable users to navigate to pages within the application. When the HyperlinkButton control is inside of a Silverlight page that is hosted in a frame, you set the NavigateUri property to a URI that maps to a page and you do not set the TargetName property. The hosting frame will navigate to the requested page. The following example shows a page with a HyperlinkButton that navigates to another page in the application.

<!-- NOTE: 
  By convention, the sdk prefix indicates a URI-based XAML namespace declaration 
  for Silverlight SDK client libraries. This namespace declaration is valid for 
  Silverlight 4 only. In Silverlight 3, you must use individual XAML namespace 
  declarations for each CLR assembly and namespace combination outside the scope 
  of the default Silverlight XAML namespace. For more information, see the help 
  topic "Prefixes and Mappings for Silverlight Libraries". 
-->
<sdk:Page x:Class="SilverlightApplication.Home" 
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" 
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" 
    xmlns:d="https://schemas.microsoft.com/expression/blend/2008" 
    xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
    xmlns:sdk="https://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
    mc:Ignorable="d" d:DesignWidth="640" d:DesignHeight="480"
    Title="Home">
    <Grid x:Name="LayoutRoot">
        <HyperlinkButton NavigateUri="/About" Content="About"></HyperlinkButton>
    </Grid>
</sdk:Page>

When the HyperlinkButton control resides outside of the frame, you can enable navigation to resources within the frame by setting the NavigateUri property to a URI that maps to a page and setting the TargetName property to the name of the frame. The following example shows a frame and two HyperlinkButton controls that reside outside of the frame. The HyperlinkButton controls facilitate navigation to pages that are displayed within the frame. The TargetName property on both HyperlinkButton controls is set to ContentFrame, which is the name assigned to the frame.

<Grid x:Name="LayoutRoot">
  <Border>
    <sdk:Frame x:Name="ContentFrame" Source="/Home">
        <sdk:Frame.UriMapper>
          <sdk:UriMapper>
            <sdk:UriMapping Uri="/{pageName}" MappedUri="/Views/{pageName}.xaml"/>
          </sdk:UriMapper>
        </sdk:Frame.UriMapper>
     </sdk:Frame>
  </Border>

  <Grid>
    <Border>
       <StackPanel>
           <HyperlinkButton NavigateUri="/Home" TargetName="ContentFrame" Content="home"/>
           <HyperlinkButton NavigateUri="/About" TargetName="ContentFrame" Content="about"/>
       </StackPanel>
    </Border>
  </Grid>
</Grid>

When you need to programmatically initiate a navigation request from a page, you must first get the NavigationService object that is used by the host frame. The NavigationService class provides members, such as GoBack, GoForward, and Source, for navigating to pages. For an example that shows a Silverlight page utilizing the NavigationService object, see the Application Navigation Example later in this topic.

Web Browser-Integrated Navigation

You can integrate the frame navigation with the Web browser navigation for a frame that is not nested inside of another frame. Browser-integrated navigation is not possible for an out-of-browser application. When integrated with the browser, the forward and back buttons of the Web browser navigate to requests within the navigation history for the top-level frame. Through the forward and back buttons of the Web browser, the user can navigate to a different Silverlight page. With browser-integrated navigation, the user can directly type a URI into the browser window and the page representing that URI is displayed in the Silverlight application. Therefore, a user can bookmark a URI or share a hyperlink that corresponds to not just the Silverlight application but the application in a specific state.

You specify whether a frame integrates with the Web browser navigation by setting the JournalOwnership property. By default, the property is set to Automatic, which means that the frame will integrate with the browser journal if it is a top-level frame. You can prevent browser-integrated navigation by setting the JournalOwnership property to OwnsJournal, which means that the frame maintains its own navigation journal.

To support navigation history, the Web page that contains the Silverlight object must include an iframe named _sl_historyFrame. By default, this iframe is included in the Web page when you create a new Silverlight application. To add navigation history to an existing application, you have to add the following code to the Web page that hosts the Silverlight object:

<iframe id="_sl_historyFrame" style='visibility:hidden;height:0;width:0;border:0px'>
</iframe>

Application Navigation Example

In an earlier example, a URI mapping was defined for the following pattern:

<uriMapper:UriMapping Uri="/ProductDetail/{productid}" 
        MappedUri="/Views/ProductDetail.xaml?ProductId={productid}"/>

The following examples use that URI mapping to enable users to navigate directly to a page that displays data about the product requested by the user. This example shows the XAML page that displays data about the product. The page also contains buttons which will initiate forward and back navigation requests from the page.

<!-- NOTE: 
  By convention, the sdk prefix indicates a URI-based XAML namespace declaration 
  for Silverlight SDK client libraries. This namespace declaration is valid for 
  Silverlight 4 only. In Silverlight 3, you must use individual XAML namespace 
  declarations for each CLR assembly and namespace combination outside the scope 
  of the default Silverlight XAML namespace. For more information, see the help 
  topic "Prefixes and Mappings for Silverlight Libraries". 
-->
<sdk:Page x:Class="NavExample.Views.ProductDetail" 
     xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" 
     xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" 
     xmlns:d="https://schemas.microsoft.com/expression/blend/2008"
     xmlns:mc="https://schemas.openxmlformats.org/markup-compatibility/2006"
     mc:Ignorable="d"
     xmlns:sdk="https://schemas.microsoft.com/winfx/2006/xaml/presentation/sdk"
     d:DesignWidth="640" d:DesignHeight="480"
     Title="Product Information">
    <StackPanel x:Name="LayoutRoot">
        <ListBox x:Name="ListBox1" ItemsSource="{Binding}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <StackPanel>
                        <TextBlock Text="{Binding Name}"/>
                        <TextBlock Text="{Binding ProductNumber}"/>
                        <TextBlock Text="{Binding Color}"/>
                        <TextBlock Text="{Binding Size}"/>
                        <TextBlock Text="{Binding ListPrice}"/>
                    </StackPanel>
                </DataTemplate>
            </ListBox.ItemTemplate>   
        </ListBox>
        <StackPanel HorizontalAlignment="Center" Orientation="Horizontal">
            <Button Width="100" x:Name="BackNavButton" Click="BackNavButton_Click" 
                    Content="&lt;&lt; back" Visibility="Collapsed" />
            <Button Width="100" x:Name="ForwardNavButton" Click="ForwardNavButton_Click" 
                    Content="forward &gt;&gt;" Visibility="Collapsed" />
        </StackPanel>
    </StackPanel>
</sdk:Page>

This example shows the code-behind page. The page accesses an WCF Data Services to retrieve data, but the steps to implement the data service are not shown here. For more information about data services, see WCF Data Services (Silverlight). The requested product id is retrieved from the query string and passed in the data service query. The navigation service for the page is used to determine if forward or back navigation is available.

Partial Public Class ProductDetail
    Inherits Page

    Public Sub New()
        InitializeComponent()
    End Sub


    Protected Overrides Sub OnNavigatedTo(ByVal e As NavigationEventArgs)
        GetProductDetail()
        SetButtonVisibility()
    End Sub

    Private Sub SetButtonVisibility()
        If (NavigationService.CanGoBack) Then
            BackNavButton.Visibility = Visibility.Visible
        Else
            BackNavButton.Visibility = Visibility.Collapsed
        End If

        If (NavigationService.CanGoForward) Then
            ForwardNavButton.Visibility = Visibility.Visible
        Else
            ForwardNavButton.Visibility = Visibility.Collapsed
        End If
    End Sub

    Private Sub BackNavButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
        If (NavigationService.CanGoBack) Then
            NavigationService.GoBack()
        End If
    End Sub

    Private Sub ForwardNavButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
        If (NavigationService.CanGoForward) Then
            NavigationService.GoForward()
        End If
    End Sub


    Private Sub GetProductDetail()
        Dim productID As String
        Dim svcContext As DataServiceContext

        svcContext = New DataServiceContext(New Uri("AdventureWorks.svc", _
                UriKind.Relative))

        If (Me.NavigationContext.QueryString.ContainsKey("ProductId")) Then
            productID = Me.NavigationContext.QueryString("ProductId")
        Else
            productID = App.Current.Resources("FeaturedProductID").ToString()
        End If

        svcContext.BeginExecute(Of Product)(New Uri("Product(" + productID + ")", _
                UriKind.Relative), AddressOf loadProductCallback, svcContext)
    End Sub
    Private Sub loadProductCallback(ByVal asyncResult As IAsyncResult)
        Dim context As DataServiceContext
        context = asyncResult.AsyncState
        ListBox1.DataContext = context.EndExecute(Of Product)(asyncResult)
    End Sub

End Class
public partial class ProductDetail : Page
{
    public ProductDetail()
    {
        InitializeComponent();
    }

    protected override void OnNavigatedTo(NavigationEventArgs e)
    {
        GetProductDetail();
        SetButtonVisibility();
    }

    private void SetButtonVisibility()
    {
        if (NavigationService.CanGoBack)
        {
            BackNavButton.Visibility = Visibility.Visible;
        }
        else
        {
            BackNavButton.Visibility = Visibility.Collapsed;
        }

        if (NavigationService.CanGoForward)
        {
            ForwardNavButton.Visibility = Visibility.Visible;
        }
        else
        {
            ForwardNavButton.Visibility = Visibility.Collapsed;
        }
    }

    private void BackNavButton_Click(object sender, RoutedEventArgs e)
    {
        if (NavigationService.CanGoBack)
        {
            NavigationService.GoBack();
        }
    }

    private void ForwardNavButton_Click(object sender, RoutedEventArgs e)
    {
        if (NavigationService.CanGoForward)
        {
            NavigationService.GoForward();
        }
    }

    private void GetProductDetail()
    {
        string productID;
        DataServiceContext svcContext = 
            new DataServiceContext(new Uri("AdventureWorks.svc", UriKind.Relative));

        if (this.NavigationContext.QueryString.ContainsKey("ProductId"))
        {
            productID = this.NavigationContext.QueryString["ProductId"];
        }
        else
        {
            productID = App.Current.Resources["FeaturedProductID"].ToString();
        }

        svcContext.BeginExecute<Product>(new Uri("Product(" + productID + ")", 
            UriKind.Relative), loadProductCallback, svcContext);
    }

    private void loadProductCallback(IAsyncResult asyncResult)
    {
        DataServiceContext context = asyncResult.AsyncState as DataServiceContext;

        ListBox1.DataContext = context.EndExecute<Product>(asyncResult);
    }
}

External Navigation

Your application can provide direct links to other Web pages. External navigation is useful to provide access to resources that are outside of your application. You can also use external navigation with Silverlight to implement controls, such as side-bar menus for use with ordinary Web pages.

In some cases, you may want to prevent any external navigation from your application. To disable all external navigation, set the enableNavigation property to none when initializing the Silverlight plug-in. For more information, see EnableNavigation (Silverlight Plug-in Object).

To enable user navigation to other Web pages, you can use the HyperlinkButton control and set the NavigateUri property to the external resource and set the TargetName property to open a new browser window.

The following code example demonstrates the use of HyperlinkButton for navigation to a location that is outside of the application.

<HyperlinkButton NavigateUri="https://www.microsoft.com"
    Content="Go to Microsoft" TargetName="_new" />

Extending the Navigation System

In Silverlight 4 and later, you can extend the navigation system by setting the ContentLoader property to a custom INavigationContentLoader implementation. The INavigationContentLoader interface defines four methods for supporting asynchronous content loading.

The default ContentLoader property value is an instance of the PageResourceContentLoader class, which loads pages from the application package (.xap file). You can replace this functionality in order to perform arbitrary URI resolution. For example, you can download pages from the server only when they are needed, or redirect certain URIs to other URIs.

The Frame and NavigationService classes also provide a Refresh method in Silverlight 4 and later. This is useful when using navigation extensions that can deliver different content for the same URI depending on user interactions in a particular page. For example, this enables some navigation scenarios with pages that require user authentication. For information on implementing INavigationContentLoader, see Additional Navigation Resources.