Navigation Overview
This topic provides an overview to navigation support in Windows Presentation Foundation (WPF), starting off with the basic building blocks, Page, Hyperlink, and Frame, before showing their use in both browser-hosted and standalone applications.
This topic contains the following sections.
- Creating a XAML Page
- Configuring a XAML Page
- Navigating Between XAML Pages
- Fragment Navigation
- Sub-Content Navigation with the Frame Class
- Loose XAML Page Considerations
- XAML Browser Applications (XBAPS)
- Navigation Service
- Navigation History
- Retaining Content State with Navigation History
- Structured Navigation
- Navigation in Standalone Applications
- Related Topics
Creating a XAML Page
In WPF, it is possible to navigate to content that can be of any .NET Framework 3.0 type, as well as HTML files. In general, however, the preferred way to package content for navigation is to use Page. You can implement a page using XAML markup:
<Page xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" />
A page that is defined in markup requires Page to be the root element and the XML namespace declaration for WPF. You add WPF content to the page by setting the Page.Content property element with the appropriate content, like so:
<Page xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Page.Content>
<!-- Page Content -->
Hello, Page!
</Page.Content>
</Page>
Page.Content can only have contain one child element; in this example, the content is a single string, "Hello, Page!". In practice, you will usually use a layout control like Grid as the child element, which will contain and compose the elements and controls of your UI.
As a short cut, you don't have to use Page.Content declaration because WPF considers the child element of a Page element to be the content. For example, the following is a valid alternative to the preceding example:
<Page xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation">
<!-- Page Content -->
Hello, Page!
</Page>
The set of WPF features that a page can use as content is only limited by security constraints (see Windows Presentation Foundation Partial Trust Security).
Once a page is defined, you can view it by:
Saving it as a file with the .xaml file extension.
Double-clicking the .xaml file.
This type of file is known as a loose (uncompiled) XAML page and, when launched from the desktop, is opened in Windows Internet Explorer, as shown in the following figure:
Note: |
---|
You can also view ad-hoc XAML with XamlPad, an application that is installed with the Windows Software Development Kit (SDK). See XAMLPad. |
For users, the fundamental experience of browsing to a XAML page from Windows Internet Explorer is exactly the same as browsing to HTML, which includes being able to launch a page from:
A web site on the local machine, the intranet, or the Internet.
A Universal Naming Convention (UNC) file share.
Local disk.
Note: |
---|
See Deploying a Windows Presentation Foundation Application for more information about publishing and launching XAML pages. |
Furthermore, a XAML page can be added to Windows Internet Explorer favorites, or as the Windows Internet Explorer home page.
Configuring a XAML Page
A XAML page is simply a host for WPF content, whose appearance is determined by the XAML elements from which it is composed, and how those elements are configured. Likewise, a XAML page can also, to some degree, determine how it appears in its host. Specifically, a XAML page can specify the title, width, and height of the window that hosts it, by setting the WindowTitle, WindowWidth, and WindowHeight attributes, respectively:
<Page
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
WindowTitle="Hello, Page Title!"
WindowWidth="250"
WindowHeight="250">
<!-- Page Content -->
Hello, Page!
</Page>
The result is shown in the following figure:
You can also give the page a title by setting its Title attribute, although the effect of doing so is only noticeable when navigating between XAML pages.
Navigating Between XAML Pages
A typical application will be comprise several pages, and the standard way to navigate through them is to use hyperlinks. In WPF, a hyperlink is implemented as the Hyperlink class, which you can declare in markup like so:
<Hyperlink NavigateUri="UriOfPageToNavigateTo.xaml">Navigate</Hyperlink>
The NavigateUri attribute specifies the uniform resource identifier (URI) of the page to navigate to, while the text content of the Hyperlink element is used to generate the link text that a user clicks to initiate the navigation. The following figure shows how a hyperlink appears in the browser:
As you would expect, clicking the link causes Windows Internet Explorer to navigate to the page that is identified by the NavigateUri property, and to add the previous page to navigation history, as shown in the following figure:
Clicking a hyperlink causes one page to navigate to another. Sometimes, there are situations where only a portion of a page needs to be navigated to. For this, you can use fragment navigation.
Fragment Navigation
You can browse to a fragment of content in either the same page, or on another page. By default, a fragment in WPF is considered a named element, which is an element that has its Name attribute set like the following:
<!-- Surrounding Content -->
<TextBlock Name="fragmentName">Fragment...</TextBlock>
<!-- Surrounding Content -->
To navigate to it, you provide a URI that includes the value of the Name attribute, and conforms to the following format:
URI#FragmentName
The following shows a hyperlink that has a URI which is configured to navigate to a fragment:
<Page
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
WindowTitle="Page That Navigates To Fragment" >
<Hyperlink NavigateUri="PageWithFragments.xaml#fragmentName">Navigate To Fragment</Hyperlink>
</Page>
A full example of fragment navigation can be found at Fragment Navigation Sample.
You can also implement your own fragment definition and behavior, which relies on handling the FragmentNavigation event. See the following:
System.Windows.Navigation.NavigationService.FragmentNavigation
System.Windows.Navigation.NavigationWindow.FragmentNavigation
Sub-Content Navigation with the Frame Class
When a page hosts sub-content that can be navigated, it can do so by adding a Frame to its content. Frame provides support for hosting and navigating between pages independently of the content that hosts the Frame. You add a frame by using the Frame element:
<Page
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
WindowTitle="FrameHostPage"
WindowWidth="250"
WindowHeight="250">
<!-- Surrounding Content -->
<Frame Source="FramePage1.xaml" />
<!-- Surrounding Content -->
</Page>
You set the Source attribute of the Frame element to specify an initial page for the Frame to navigate to (this is the equivalent of NavigateUri). The following figure shows the frame navigating between several pages:
You'll notice that the navigation history entry name of each page is its path; because the pages are hosted by a frame, rather than a window, WindowTitle is not applied. Instead, you set the Title attribute of the Page element to customize the navigation history entry name:
<Page
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
WindowWidth="250"
WindowHeight="250"
Title="FramePage1">
<!-- Content -->
</Page>
The following figure shows the result:
By default, the navigation history for a frame is recorded by Windows Internet Explorer for every page navigation, whether the page is hosted by Windows Internet Explorer or hosted by a frame inside a page. Sometimes, this level of navigation is too fine-grained; for example, consider the following figure:
In this type of scenario, you may be interested in doing the following:
Storing the navigations between the pages hosted by the browser in navigation history
Giving the pages that are hosted in the frame their own navigation history.
In these cases, you can configure the frame to use its own navigation history by setting the JournalOwnership attribute (JournalOwnership) of the Frame to OwnsJournal (OwnsJournal):
<Page
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
WindowTitle="FrameHostPage"
WindowWidth="250"
WindowHeight="250">
<!-- Surrounding Content -->
<Frame ... JournalOwnership="OwnsJournal" />
<!-- Surrounding Content -->
</Page>
The following figure illustrates the effect of navigating within a frame that uses its own navigation history:
Notice that the navigation UI in the frame shows that navigation has occurred, while the browser has not.
If you want the frame to provide its own navigation history, but don't want to show the navigation UI, you can change the value of the NavigationUIVisibility (NavigationUIVisibility) attribute on the Frame element from the default value of Automatic (Automatic) to Hidden:
<Page
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
WindowTitle="FrameHostPage"
WindowWidth="250"
WindowHeight="250">
<!-- Surrounding Content -->
<Frame ... NavigationUIVisibility="Hidden" />
<!-- Surrounding Content -->
</Page>
Loose XAML Page Considerations
While loose XAML pages can take advantage of the richness of the WPF platform, there are several considerations to make when using them:
While being able to define event triggers, you can't handle any events that are raised by the types that compose the XAML.
You can't use code to customize behavior or implement custom business logic.
It is not possible to use some of the application services provide by WPF, such as application lifetime management, shared state, and processing unhandled exceptions.
It is not possible to use services provided by the .NET Framework 3.0 such as data persistence and web services.
Each loose XAML page is treated as a separate application by WPF. This means that the WPF infrastructure that allows Windows Internet Explorer to host a loose XAML page is loaded each time Windows Internet Explorer navigates to a loose XAML page.
Note: |
---|
Navigating from one page to another within a frame that is hosted by a page in Windows Internet Explorer is not considered navigation to a new application, since the WPF infrastructure is already running. |
If any or all of these considerations are issues for your application, you can use XBAPs.
XAML Browser Applications (XBAPS)
Unlike a loose XAML application, which consists entirely of loose XAML pages, an XBAP is a compiled .NET Framework 3.0 application that runs from within a browser-style host like Windows Internet Explorer or Microsoft Windows Media Center (WMC). Like loose XAML pages, XBAPs can use both the .NET Framework 3.0 and the Windows Presentation Foundation (WPF), subject only to security restrictions (see Windows Presentation Foundation Partial Trust Security).
The following figure shows an XBAP application being hosted in Windows Internet Explorer 7:
Note: |
---|
This figure is a screenshot of the ExpenseIt sample application that comes with the Windows Software Development Kit (SDK). See ExpenseIt Demo. |
As you can see, an XBAP looks like any other type of browser-hosted application, which is a result of the special support for integration that XBAPs have with Internet Explorer 7, which includes:
Pages that are navigated in the XBAP are added to the host browser's navigation UI.
XBAP pages that are selected from the host browser's navigation UI are navigated to in the XBAP.
An XBAP remains in navigation history when the host browser is navigated away from the XBAP.
The Page Definition
As with other visual types, like Window, a page is usually implemented using both markup, to implement the appearance, and code-behind, to implement the appearance. The following shows the markup and code-behind definition for a home page:
<Page
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
x:Class="HomePage"
WindowTitle="XBAP HomePage" >
Hello from the XBAP HomePage!
</Page>
using System;
using System.Windows.Controls;
public partial class HomePage : Page
{
public HomePage()
{
InitializeComponent();
}
}
To enable a markup file and code-behind file to work together, the following is required:
In markup, the Page element must include the x:Class attribute, which instructs MSBuild to create a partial class for the markup file when the project is built with the name specified by the x:Class attribute. This requires the addition of an XML namespace declaration for the XAML schema (xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"). The partial 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 needs to derive from Page. This allows the code-behind file to be associated with the partial class that is generated for the markup file when it is built (see Building a Windows Presentation Foundation Application).
In code-behind, the 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 defined using both markup and code-behind, and includes the necessary configuration to create the association between the markup and code-behind files as described here. |
The Application Definition
Once you have one or more pages implemented, you need to specify one as the page that is first shown when the XBAP is launched. To do this, you need to call StartupUri. This requires that you implement an application definition, which all XBAPs require. The following shows a basic application definition that comprises both markup and code-behind:
<Application
x:Class="App"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml" />
using System;
using System.Windows;
public partial class App : Application { }
The markup part of the application definition needs to be specified as an Microsoft build engine (MSBuild) ApplicationDefinition item. For a detailed discussion of application definitions, see Application Management Overview.
Configuring a Start Page
The basic application definition for an XBAP is the same as the basic application definition for a standalone application. And, much of the behavior that is provided by an application definition is common to both types of applications too. For example, you set the StartupUri property to specify the initial page to show:
<Application
x:Class="App"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="HomePage.xaml" />
Setting StartupUri results in the XBAP being launched in Internet Explorer and HomePage.xaml being automatically navigated to, as shown in the following figure:
On the surface, browsing to a page in an XBAP is the same as browsing to a loose XAML page. Under the surface, things are different: a page has a lifetime that you can track.
Page Lifetime
The lifetime of a page is dictated by the following:
Whether a page is being navigated to for the first time, or is being navigated to a subsequent time from navigation history.
How a page is configured for storage in navigation history.
Whether a page is defined using only code.
The first time that a page is browsed using a URI, WPF instantiates the page before navigating to it. Subsequently, the Loaded event is raised, after which the Unloaded event is raised when the page is navigated away from. The following figure illustrates these events:
The same events fire when the page is subsequently browsed back to using navigation history, which is a side-effect of the default navigation history behavior that is employed by WPF. To avoid excessive memory consumption, WPF does not store page instances in navigation history. Instead, WPF stores page metadata that WPF uses to create a new instance of the page, rather than simply opening the previous instance; essentially, each page that is navigated to is a new instance. The only case where the behavior is different is when you navigate pages that are defined using code only; instances of these types of pages are always retained in navigation history.
Creating a new instance of a page each time it is navigated to poses problems for maintaining state across the two instances; if a user enters data on a page the first time they browse to it, they will want to see that data the next time they browse to the page. WPF provides several levels of support for managing state, and an overview is provide later in this topic (see Retaining Content State with Navigation History).
Note: |
---|
Pages in an XBAP that are kept alive will be removed from navigation history when the XBAP is navigated away from. |
In general, you should prefer the default page behavior of not keeping pages alive, and use custom content state management support to retain only state across page navigations, as discussed later in this topic.
Navigation Lifetime
When a page is navigated to, not only can you hook into the lifetime of the page, but you can also track the lifetime of a single navigation, to both monitor and manipulate it. For navigation from within an XBAP, you can track a navigation by handling the following events that are raised by the Application class:
Navigating. Occurs when a new navigation is requested.
Navigated. Occurs when the content that is being navigated to has been found, although it may not have completed loading.
NavigationProgress. Occurs periodically during a download to provide navigation progress information.
NavigationFailed. Occurs when an error is raised while navigating to the requested content.
NavigationStopped. Occurs when the navigation is stopped, or when a new navigation is requested while a current navigation is in progress.
LoadCompleted. Occurs when content that was navigated to has been loaded, parsed, and has begun rendering.
FragmentNavigation. Occurs when navigation to a content fragment begins, which occurs immediately, if the desired fragment is in the current content, or after the source content has been loaded, if the desired fragment is in different content.
These are raised in the order that is illustrated by the following figure:
Application provides a centralized way to track navigations within an application, but it is not the only way. Application doesn't actually raise the navigation events for any navigation, nor does it handle navigation requests. Instead, this is the job of the NavigationService.
Navigation Service
When a hyperlink is clicked, WPF essentially does the following:
Gets the value of the NavigateUri attribute.
Locates the page that the URI refers to.
Loads the page.
Displays the page.
Locating a page and loading a page are the essential steps of a navigation that, in WPF, is performed by the NavigationService class. The key members that are implemented by NavigationService fall into the following categories:
Navigation: Navigate, Refresh, StopLoading, Content, Source, CurrentSource.
Navigation History: GoBack, GoForward, CanGoBack, CanGoForward, AddBackEntry, RemoveBackEntry.
Navigation Lifetime: Navigating, Navigated, NavigationProgress, NavigationFailed, NavigationStopped (raised when StopLoading is called), LoadCompleted, FragmentNavigation.
Note: |
---|
See System.Windows.Navigation.NavigationService for detailed information on these members. |
When a hyperlink is clicked WPF calls System.Windows.Navigation.NavigationService.Navigate(System.Uri) to locate and load the page at the specified URI, raises the lifetime events as appropriate throughout the navigation, loads the page into the Content property, and updates Source and CurrentSource. When you handle the Application navigation events, you are really handling the NavigationService navigation events; Application is simply a proxy for these events on behalf of NavigationService. This serves to reinforce that NavigationService is the only class that actually does navigation in WPF.
Note: |
---|
A Hyperlink with NavigateUri set will only work in the presence of a NavigationService. This is why a Hyperlink that is part of the content of a Window doesn't navigate. |
You don't need to know about NavigationService if the navigation in your application is built declaratively using hyperlinks in XAML, since WPF will use the NavigationService on your behalf to navigation when a hyperlink is clicked.
Although, there are situations when you will need to use the NavigationService, 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 an application can only determine which page it needs to navigate to at run time.
In these situations, you'll need to write code to perform your navigation, which means programming the NavigationService. This involves acquiring a reference to the NavigationService and calling System.Windows.Navigation.NavigationService.Navigate(System.Object), using code like the following:
<Page
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
x:Class="NSNavigationPage"
WindowTitle="Navigation Service Navigation Page" >
<Hyperlink Click="hyperlink_Click">Navigate to Page with Non-Default Constructor</Hyperlink>
</Page>
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
public partial class NSNavigationPage : Page
{
public NSNavigationPage()
{
InitializeComponent();
}
void hyperlink_Click(object sender, RoutedEventArgs e)
{
// Instantiate page to navigate to
PageWithNonDefaultConstructor page = new PageWithNonDefaultConstructor(DateTime.Now);
// Get navigation service
NavigationService ns = NavigationService.GetNavigationService(this);
ns.Navigate(page);
}
}
<Page
x:Class="SDKSample.PageWithNonDefaultConstructor"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
Title="PageWithNonDefaultConstructor">
</Page>
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
public partial class PageWithNonDefaultConstructor : Page
{
public PageWithNonDefaultConstructor(DateTime dateTimeInstantiated)
{
InitializeComponent();
this.Content = "Instantiated at " + dateTimeInstantiated.ToString();
}
}
Here, the code uses the static GetNavigationService property to get a reference to its NavigationService.
Note: |
---|
An XBAP uses a NavigationWindow to handle navigation, and to integrate with Internet Explorer 7, with respect to navigation history. The navigation service that the page in the preceding example returns is the NavigationService that is used by the NavigationWindow. See System.Windows.Navigation.NavigationService. |
Note also that the code uses an overload of the Navigate method that accepts an object rather than a URI.
It is possible to navigate to content, whether by URI or an actual object, by setting Source and Content, respectively. The following code shows how to use Source to do so:
void hyperlink_Click(object sender, RoutedEventArgs e)
{
// Causes NavigationService to navigate to page at specified URI.
// NavigationService.CurrentSource will have the URI of the page being
// navigate from until the navigation is complete.
NavigationService ns = NavigationService.GetNavigationService(this);
ns.Source = new Uri("AnotherPage.xaml", UriKind.Relative);
}
The next piece of code shows using Content to same effect:
void hyperlink_Click(object sender, RoutedEventArgs e)
{
// Causes NavigationService to navigate to specified page object.
// From a navigation history perspective, navigating to an object
// has the same effect as navigating via URI ie the page navigating
// from is added to navigation history.
NavigationService ns = NavigationService.GetNavigationService(this);
ns.Content = new AnotherPage();
}
All code in an XBAP can use this technique, although Page implements a helper NavigationService property as a shortcut:
void hyperlink_Click(object sender, RoutedEventArgs e)
{
// Instantiate page to navigate to
PageWithNonDefaultConstructor page =
new PageWithNonDefaultConstructor(DateTime.Now);
// Get navigation service
this.NavigationService.Navigate(page);
}
When a page navigates to itself, this is considered a fragment navigation, and the page is scrolled to the top. But, you can force the current page to reload by calling Refresh:
void refreshHyperlink_Click(object sender, RoutedEventArgs e)
{
// Reload the current page.
this.NavigationService.Refresh();
}
Whether a hyperlink is clicked or the NavigationService is used programmatically, an entry is placed in navigation history for the page that is navigated away from.
Navigation History
Recall from earlier that navigation between loose XAML pages is record by Internet Explorer in navigation history:
For loose XAML pages, navigation history is managed by Internet Explorer, since Internet Explorer is doing the navigation. For XBAPs, though, Internet Explorer simply navigates to the XBAP - navigation within an XBAP is managed by WPF instead, using a forward stack, a back stack, and supporting functionality. These features are collectively referred to as navigation history (or "the journal").
As with NavigationService, if your navigation is specified declaratively in markup (a hyperlink, for example), you are unlikely to be concerned with navigation history. This is especially true because WPF integrates its navigation history with the navigation history in Internet Explorer. Visually, this causes the navigation UI in Internet Explorer to operate the same way for page navigations in an XBAP as it does for navigation between loose XAML pages.
If you'd like to extend the navigation UI in with additional navigation support on your pages, you can still do so declaratively, using NavigationCommands, as shown in the following code:
<Page
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
WindowTitle="Navigation Commands Page">
<TextBlock>
<Hyperlink Command="NavigationCommands.BrowseBack">Back</Hyperlink>
<LineBreak />
<Hyperlink Command="NavigationCommands.BrowseForward">Forward</Hyperlink>
</TextBlock>
</Page>
However, if you need programmatic access to navigation history, NavigationService provides the following four members for browsing through navigation history:
GoBack.
GoForward.
CanGoBack.
CanGoForward.
NavigationService also implements two members that allow you to manipulate the contents of navigation history: AddBackEntry and RemoveBackEntry. In general, you should be cautious when using either of these methods as you may disrupt the typical flow of navigation that a user would expect. One good reason to use AddBackEntry, though, is to retain page state across multiple navigations to the same page.
Retaining Content State with Navigation History
As discussed in Page Lifetime, the default behavior when browsing to a page is to instantiate a new instance of the page. In general, most of the content displayed from a page is static. Consequently, it doesn't make sense to keep copies of those pages in memory, particularly when static content includes large media files.
From a user experience point of view, though, when a user enters data into one page, browses to another, than browses back to the original page, they expect to see the data they entered previously. Fortunately, WPF provides a mechanism for remembering page state across page navigations that leverages navigation history.
The following details the process of navigating to a new page:
An entry for the current page is added to navigation history.
State from the page is associated with the entry.
The new page is navigated to.
When the original page is browsed back to using navigation history, the following steps take place:
The original page, as specified by the entry in back navigation history, is instantiated.
The page is refreshed with the state that was associated with the navigation history entry for the page.
The page is navigated back to.
State is not automatically stored, however; some additional implementation is required to associate state with a navigation history entry. Fortunately, WPF takes on this implementation burden for the following controls, including:
Frame
Consequently, if pages use these controls, data entered into those controls is automatically remembered, as shown in the following figure:
When you use custom types to encapsulate your page state, you'll need to use alternative methods for storing that state across page instances. WPF provides alternative mechanisms that you can use to do so.
The first option is to force WPF to retain an instance of the page in navigation history by setting the KeepAlive attribute of a page to true, like so:
<Page
x:Class="SDKSample.KeepAlivePage"
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
WindowTitle="Keep Alive Page"
KeepAlive="True">
My instance is stored in navigation history.
</Page>
While the main advantage is that state is remembered across navigations since the instance of the page is stored in navigation history, the page will load faster since it has already been constructed, although the tradeoff is memory consumption.
Still, you can reduce the memory cost by not keeping your pages alive and remembering only small pieces of state per-page using dependency properties that are configured to retain state across navigations (see DependencyProperty). A retained-state dependency property is configured with the following metadata flag:
System.Windows.FrameworkPropertyMetadata.Journal
The following code example shows an example of a retained-state dependency property:
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
public partial class RetainedStateDPPage : System.Windows.Controls.Page
{
// Retained-State property
public static readonly DependencyProperty RetainedStateDP;
static RetainedStateDPPage()
{
// Register the local property with the retained-state dependency property
RetainedStateDPPage.RetainedStateDP =
DependencyProperty.Register(
"RetainedState",
typeof(string),
typeof(RetainedStateDPPage),
new FrameworkPropertyMetadata(
null,
FrameworkPropertyMetadataOptions.Journal));
}
public RetainedStateDPPage()
{
InitializeComponent();
}
// Property to register with the retained-state property
public string RetainedState
{
get
{
return (string)base.GetValue(RetainedStateDPPage.RetainedStateDP);
}
set
{
base.SetValue(RetainedStateDPPage.RetainedStateDP, value);
}
}
}
The controls that retained state across page navigations automatically that were listed earlier utilize this same technique.
With the property declared, you get and set it as appropriate. For a complete example of both declaring and using a retained-state dependency property to remember which control had the focus, see Remember A Single Item of State Across Page Instances.
If the state that your page needs to remember across navigations comprises multiple pieces of data, you may find it easier to implement IProvideCustomContentState (see Remember A Single Set of State Across Page Instances).
In some situations, you may find that you need to remember and navigate through multiple pieces of state for a single page. This can happen when you have a page that navigates back to show the last piece of state. Using IProvideCustomContentState and AddBackEntry, you can design this type of page so that you can "replay" its previous states without actually navigating from the current instance of the page (see Remember Multiple Sets of State Per Page Instance).
Important: |
---|
When you retain state for a page, you cannot store a reference to the page, or any part of it. Doing so prevents WPF from releasing the page instance, and defeats the purpose of the default navigation history behavior. If you must do this, you might consider using KeepAlive instead. |
Structured Navigation
Throughout this topic, we've discussed page navigation using hyperlinks. One issue with this traditional type of navigation is the difficulty involved with passing data between pages, particularly if multiple pages operate over a single piece of data in sequence, such as a wizard. For this type of navigation, WPF provides the PageFunction class. You can find a complete overview of the PageFunction class in Structured Navigation Overview.
Navigation in Standalone Applications
Although pages, hyperlinks, and frames are naturally suited as browser-hosted applications, these elements are not only useful when hosted inside a browser. Modern operating systems and Windows applications often tend to leverage the experience of a large number of users with browser applications to incorporate browser-style navigation.
Common examples of browser-style navigation that is not hosted by a browser include the following:
Word Thesaurus: Navigate back and forwards through word choices.
File Explorer: Navigating back and forwards through selected files and folders.
Wizards: Doing a process using one or more atomic tasks that can be navigated back and forward between eg Windows Components Wizard to add/remove Windows installation items such as Internet Information Server (IIS).
The choice around whether to use browser-style navigation in standalone applications depends primarily on the user experience. Uses can benefit if they are primarily experienced with using browsers, or if the type of functionality is more naturally suited to navigation, such as ad-hoc browsing, document reading etc.
There are two ways in which navigation can be incorporated into a standalone application: NavigationWindow and Frame. Both of these are known as navigators.
Navigators
A navigator is a class that navigate to and display content. NavigationWindow is a window that provides these navigation services, while Frame provides sub-content navigation in exactly the same way it did for loose XAML pages, which we saw earlier.
To host and navigate content, both NavigationWindow and Frame use their own instance of the NavigationService for raw navigation, and both provide navigation history by using the journal. The basic construction of a navigation is shown in the following figure:
Essentially, this allows NavigationWindow and Frame to provide the same experience for pages as an XBAP does.
Note: |
---|
For all intents and purposes, and from the perspective of a page, the combination of Internet Explorer and XBAP can also be considered a navigator. In reality, XBAPs use a NavigationWindow and, implicitly, its associated NavigationService and navigation history. The navigation history integrates with the browser's own navigation history. |
Besides using NavigationService and navigation history, both classes implement the same members that NavigationService implements. This allows you to program navigation support directly against them. You may consider this if you need to provide a custom navigation UI for a Frame that is hosted in a Window.
Furthermore, both types implement additional, navigation-related members, including BackStack (System.Windows.Navigation.NavigationWindow.BackStack, System.Windows.Controls.Frame.BackStack) and ForwardStack (System.Windows.Navigation.NavigationWindow.ForwardStack, System.Windows.Controls.Frame.ForwardStack), which allow you to enumerate the entries in back and forward navigation history, respectively.
The following topics provide an overview of both NavigationWindow and Frame.
The NavigationWindow Class
If your standalone application is wholly browser-style, or one of the application windows is wholly browser-style, you should use NavigationWindow to host it. One example might be to create a wizard, which is shown in Wizard Sample. To navigate a NavigationWindow to a page, you simply set its Source property:
<NavigationWindow ... Source="HomePage.xaml" />
This causes NavigationWindow to the specified content, as shown in the following figure.
If your custom NavigationWindow doesn't require code-behind, consider setting StartupUri with the uniform resource identifier (URI) to the desired page. Application will instantiate NavigationWindow and set its source to the page that you specified. See Application Management Overview for more information.
As you can see from the preceding figure, NavigationWindow shows a navigation UI that allows users to navigate back and forward through navigation history. If you need to hide the navigation UI, you can set the ShowsNavigationUI property to false:
<NavigationWindow ... ShowsNavigationUI="False" />
One reason you may want to do this is to allow your pages to provide a custom navigation UI. Alternatively, you can use the capabilities of WPF to replace the UI of the NavigationWindow itself. See Custom NavigationWindow Chrome Sample for an example of how to do so.
Frame in Standalone Applications
If your standalone application has windows that require sub-content, you can use Frame in the same way as you saw earlier for loose XAML pages.
See Frame Navigation in a Window Sample provides an introduction into the use of Frame.
See Also
Concepts
Application Management Overview
Pack URIs in Windows Presentation Foundation
Windows Presentation Foundation Windows Media Center-Hosted Applications Overview
Structured Navigation Overview
Deploying a Windows Presentation Foundation Application
Other Resources
Remember A Single Set of State Across Page Instances
Remember Multiple Sets of State Per Page Instance
Remember A Single Item of State Across Page Instances
Wizard Sample
Custom NavigationWindow Chrome Sample
Frame Navigation in a Window Sample