
Navigation in WPF Applications
This topic provides an overview of the key navigation capabilities in WPF. These capabilities are available to both standalone applications and XBAPs, although this topic presents them within the context of an XBAP.
This section explains and demonstrates the following aspects of navigation:
Implementing a Page
In WPF, you can navigate to several content types that include .NET Framework objects, custom objects, enumeration values, user controls, XAML files, and HTML files. However, you'll find that the most common and convenient way to package content is by using Page. Furthermore, Page implements navigation-specific features to enhance their appearance and simplify development.
Using Page, you can declaratively implement a navigable page of XAML content by using markup like the following.
|
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" />
|
A Page that is implemented in XAML markup has Page as its root element and requires the WPF XML namespace declaration. The Page element contains the content that you want to navigate to and display. You add content by setting the Page.Content property element, as shown in the following markup.
|
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Page.Content>
<!-- Page Content -->
Hello, Page!
</Page.Content>
</Page>
|
Page.Content can only contain one child element; in the preceding example, the content is a single string, "Hello, Page!" In practice, you will usually use a layout control as the child element (see The Layout System) to contain and compose your content.
The child elements of a Page element are considered to be the content of a Page and, consequently, you don't need to use the explicit Page.Content declaration. The following markup is the declarative equivalent to the preceding sample.
|
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<!-- Page Content -->
Hello, Page!
</Page>
|
In this case, Page.Content is automatically set with the child elements of the Page element. For more information, see WPF Content Model.
A markup-only Page is useful for displaying content. However, a Page can also display controls that allow users to interact with the page, and it can respond to user interaction by handling events and calling application logic. An interactive Page is implemented by using a combination of markup and code-behind, as shown in the following example.
|
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.HomePage">
Hello, from the XBAP HomePage!
</Page>
|
|
using System.Windows.Controls; // Page
namespace SDKSample
{
public partial class HomePage : Page
{
public HomePage()
{
InitializeComponent();
}
}
}
|
To allow a markup file and code-behind file to work together, the following configuration is required:
In markup, the Page element must include the x:Class attribute. When the application is built, the existence of x:Class in the markup file causes Microsoft build engine (MSBuild) to create a partial class that derives from Page and has the name that is specified by the x:Class attribute. This requires the addition of an XML namespace declaration for the XAML schema (xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"). The generated partial class implements InitializeComponent, which is called to register the events and set the properties that are implemented in markup.
In code-behind, the class must be a partial class with the same name that is specified by the x:Class attribute in markup, and it must derive from Page. This allows the code-behind file to be associated with the partial class that is generated for the markup file when the application is built (see Building a WPF Application (WPF)).
In code-behind, the Page class must implement a constructor that calls the InitializeComponent method. InitializeComponent is implemented by the markup file's generated partial class to register events and set properties that are defined in markup.
Note: |
|---|
When you add a new
Page to your project using Microsoft Visual Studio, the Page is implemented using both markup and code-behind, and it includes the necessary configuration to create the association between the markup and code-behind files as described here.
|
Once you have a Page, you can navigate to it. To specify the first Page that an application navigates to, you need to configure the start Page.
Configuring a Start Page
XBAPs require a certain amount of application infrastructure to be hosted in a browser. In WPF, the Application class is part of an application definition that establishes the required application infrastructure (see Application Management Overview).
An application definition is usually implemented using both markup and code-behind, with the markup file configured as an MSBuild ApplicationDefinition item. The following is an application definition for an XBAP.
|
<Application
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.App" />
|
|
using System.Windows; // Application
namespace SDKSample
{
public partial class App : Application { }
}
|
An XBAP can use its application definition to specify a start Page, which is the Page that is automatically loaded when the XBAP is launched. You do this by setting the StartupUri property with the uniform resource identifier (URI) for the desired Page.
Note: |
|---|
In most cases, the
Page is either compiled into or deployed with an application. In these cases, the URI that identifies a Page is a pack URI, which is a URI that conforms to the pack scheme. Pack URIs are discussed further in Pack URIs in Windows Presentation Foundation. You can also navigate to content using the http scheme, which is discussed below.
|
You can set StartupUri declaratively in markup, as shown in the following example.
|
<Application
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.App"
StartupUri="PageWithHyperlink.xaml" />
|
In this example, the StartupUri attribute is set with a relative pack URI that identifies HomePage.xaml. When the XBAP is launched, HomePage.xaml is automatically navigated to and displayed. This is demonstrated by the following figure, which shows an XBAP that was launched from a Web server.
.png)
Configuring the Host Window's Title, Width, and Height
One thing you may have noticed from the previous figure is that the title of both the browser and the tab panel is the URI for the XBAP. Besides being long, the title is neither attractive nor informative. For this reason, Page offers a way for you to change the title by setting the WindowTitle property. Furthermore, you can configure the width and height of the browser window by setting WindowWidth and WindowHeight, respectively.
WindowTitle, WindowWidth, and WindowHeight can be set declaratively in markup, as shown in the following example.
|
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.HomePage"
WindowTitle="Page Title"
WindowWidth="500"
WindowHeight="200">
Hello, from the XBAP HomePage!
</Page>
|
The result is shown in the following figure.
.png)
Hyperlink Navigation
A typical XBAP comprises several pages. The simplest way to navigate from one page to another is to use a Hyperlink. You can declaratively add a Hyperlink to a Page by using the Hyperlink element, which is shown in the following markup.
|
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
WindowTitle="Page With Hyperlink"
WindowWidth="250"
WindowHeight="250">
...
<Hyperlink NavigateUri="UriOfPageToNavigateTo.xaml">
Navigate to Another Page
</Hyperlink>
...
</Page>
|
A Hyperlink element requires the following:
The pack URI of the Page to navigate to, as specified by the NavigateUri attribute.
Content that a user can click to initiate the navigation, such as text and images (for the content that the Hyperlink element can contain, see Hyperlink).
The following figure shows an XBAP with a Page that has a Hyperlink.
.png)
As you would expect, clicking the Hyperlink causes the XBAP to navigate to the Page that is identified by the NavigateUri attribute. Additionally, the XBAP adds an entry for the previous Page to the Recent Pages list in Internet Explorer 7. This is shown in the following figure.
.png)
As well as supporting navigation from one Page to another, Hyperlink also supports fragment navigation.
Fragment Navigation
Fragment navigation is the navigation to a content fragment in either the current Page or another Page. In WPF, a content fragment is the content that is contained by a named element. A named element is an element that has its Name attribute set. The following markup shows a named TextBlock element that contains a content fragment.
|
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
WindowTitle="Page With Fragments" >
...
<!-- Content Fragment called "Fragment1" -->
<TextBlock Name="Fragment1">
Ea vel dignissim te aliquam facilisis ...
</TextBlock>
...
</Page>
|
For a Hyperlink to navigate to a content fragment, the NavigateUri attribute must include the following:
A fragment URI has the following format.
PageURI#ElementName
The following shows an example of a Hyperlink that is configured to navigate to a content fragment.
|
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
WindowTitle="Page That Navigates To Fragment" >
...
<Hyperlink NavigateUri="PageWithFragments.xaml#Fragment1">
Navigate To pack Fragment
</Hyperlink>
...
</Page>
|
For a complete example of fragment navigation, see Fragment Navigation Sample.
Note: |
|---|
This section describes the default fragment navigation implementation in
WPF. WPF also allows you to implement your own fragment navigation scheme which, in part, requires handling the NavigationService..::.FragmentNavigation event.
|
Important Note: |
|---|
You can navigate to fragments in loose
XAML pages (markup-only XAML files with Page as the root element) only if the pages can be browsed via HTTP.
However, a loose
XAML
page can navigate to its own fragments.
|
Navigation Service
Programmatic Navigation with the Navigation Service
You don't need to know about NavigationService if navigation is implemented declaratively in markup using Hyperlink, because Hyperlink uses the NavigationService on your behalf. This means that, as long as either the direct or indirect parent of a Hyperlink is a navigation host (see Navigation Hosts), Hyperlink will be able to find and use the navigation host's navigation service to process a navigation request.
However, there are situations when you need to use NavigationService directly, including the following:
When you need to instantiate a Page using a non-default constructor.
When you need to set properties on the Page before you navigate to it.
When the Page that needs to be navigated to can only be determined at run time.
In these situations, you need to write code to programmatically initiate navigation by calling the Navigate method of the NavigationService object. That requires getting a reference to a NavigationService.
Getting a Reference to the NavigationService
Programmatic Navigation to a Page Object
The following example shows how to use the NavigationService to programmatically navigate to a Page. Programmatic navigation is required because the Page that is being navigated to can only be instantiated using a single, non-default constructor. The Page with the non-default constructor is shown in the following markup and code.
|
<Page
x:Class="SDKSample.PageWithNonDefaultConstructor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="PageWithNonDefaultConstructor">
<!-- Content goes here -->
</Page>
|
|
using System.Windows.Controls; // Page
namespace SDKSample
{
public partial class PageWithNonDefaultConstructor : Page
{
public PageWithNonDefaultConstructor(string message)
{
InitializeComponent();
this.Content = message;
}
}
}
|
The Page that navigates to the Page with the non-default constructor is shown in the following markup and code.
|
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.NSNavigationPage">
<Hyperlink Click="hyperlink_Click">
Navigate to Page with Non-Default Constructor
</Hyperlink>
</Page>
|
|
using System.Windows; // RoutedEventArgs
using System.Windows.Controls; // Page
using System.Windows.Navigation; // NavigationService
namespace SDKSample
{
public partial class NSNavigationPage : Page
{
public NSNavigationPage()
{
InitializeComponent();
}
void hyperlink_Click(object sender, RoutedEventArgs e)
{
// Instantiate the page to navigate to
PageWithNonDefaultConstructor page = new PageWithNonDefaultConstructor("Hello!");
// Navigate to the page, using the NavigationService
this.NavigationService.Navigate(page);
}
}
}
|
When the Hyperlink on this Page is clicked, navigation is initiated by instantiating the Page to navigate to using the non-default constructor and calling the NavigationService..::.Navigate method. Navigate accepts a reference to the object that the NavigationService will navigate to, rather than a pack URI.
Programmatic Navigation with a Pack URI
If you need to construct a pack URI programmatically (when you can only determine the pack URI at run time, for example), you can use the NavigationService..::.Navigate method. This is shown in the following example.
|
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.NSUriNavigationPage">
<Hyperlink Click="hyperlink_Click">Navigate to Page by Pack URI</Hyperlink>
</Page>
|
|
using System; // Uri, UriKind
using System.Windows; // RoutedEventArgs
using System.Windows.Controls; // Page
using System.Windows.Navigation; // NavigationService
namespace SDKSample
{
public partial class NSUriNavigationPage : Page
{
public NSUriNavigationPage()
{
InitializeComponent();
}
void hyperlink_Click(object sender, RoutedEventArgs e)
{
// Create a pack URI
Uri uri = new Uri("AnotherPage.xaml", UriKind.Relative);
// Get the navigation service that was used to
// navigate to this page, and navigate to
// AnotherPage.xaml
this.NavigationService.Navigate(uri);
}
}
}
|
Refreshing the Current Page
A Page is not downloaded if it has the same pack URI as the pack URI that is stored in the NavigationService..::.Source property. To force WPF to download the current page again, you can call the NavigationService..::.Refresh method, as shown in the following example.
|
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.NSRefreshNavigationPage">
<Hyperlink Click="hyperlink_Click">Refresh this page</Hyperlink>
</Page>
|
|
using System.Windows; // RoutedEventArgs
using System.Windows.Controls; // Page
using System.Windows.Navigation; // NavigationService
namespace SDKSample
{
public partial class NSRefreshNavigationPage : Page
{
...
void hyperlink_Click(object sender, RoutedEventArgs e)
{
// Force WPF to download this page again
this.NavigationService.Refresh();
}
}
}
|
Navigation Lifetime
There are many ways to initiate navigation, as you've seen. When navigation is initiated, and while navigation is in progress, you can track and influence the navigation using the following events that are implemented by NavigationService: