本主题提供 WPF 中的关键导航功能的概述。虽然在本主题中,这些功能是以 XBAP 为例介绍的,但它们既可用于 XBAP,也可用于独立应用程序。
本节说明并演示导航的以下方面:
实现页
在 WPF 中,可以导航到多种内容,包括 .NET Framework 对象、自定义对象、枚举值、用户控件、XAML 文件和 HTML 文件。但是,打包内容的最常用和最方便的方式还是使用 Page。另外,Page 还实现了特定于导航的功能,以增强其外观并简化开发过程。
利用 Page,可以使用类似于下面的标记来以声明方式实现 XAML 内容的可导航页。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" />
在 XAML 标记中实现的 Page 将 Page 作为其根元素,并且需要 WPF XML 命名空间声明。Page 元素包含要导航到并显示的内容。通过设置 Page.Content 属性元素可以添加内容,如下面的标记所示。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Page.Content>
<!-- Page Content -->
Hello, Page!
</Page.Content>
</Page>
Page.Content 只能包含一个子元素;在前面的示例中,内容为单个字符串“Hello, Page!”。实际上,通常使用布局控件作为子元素(请参见布局系统)来包含和创作内容。
Page 元素的子元素视作 Page 的内容,因此,不需要使用显式 Page.Content 声明。下面是与上例等效的声明性标记。
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<!-- Page Content -->
Hello, Page!
</Page>
此例中,以 Page 元素的子元素自动设置了 Page.Content。有关更多信息,请参见 WPF 内容模型。
纯标记 Page 对于显示内容很有用。但是,Page 也可显示允许用户与页进行交互的控件,它还可通过处理事件和调用应用程序逻辑来响应用户交互。交互式 Page 是使用标记和代码隐藏的组合实现的,如下面的示例所示。
为了使标记文件和代码隐藏文件配合工作,需要以下配置:
在标记中,Page 元素必须包含 x:Class 属性。生成应用程序时,标记文件中如果存在 x:Class,则 Microsoft Build Engine (MSBuild) 将创建一个从 Page 派生的 partial 类,并且该类的名称是由 x:Class 属性指定的。这要求添加 XAML 架构的 XML 命名空间声明 (xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml")。生成的 partial 类实现 InitializeComponent,调用该方法可注册事件并设置在标记中实现的属性。
在代码隐藏中,该类必须是 partial 类,并且具有标记中的 x:Class 属性所指定的名称,还必须是从 Page 派生的。这样,代码隐藏文件就与应用程序生成时为标记文件生成的 partial 类相关联(请参见生成 WPF 应用程序 (WPF))。
在代码隐藏中,Page 类必须实现调用 InitializeComponent 方法的构造函数。InitializeComponent 是由标记文件的生成的 partial 类实现的,用来注册事件和设置在标记中定义的属性。
说明: |
|---|
在使用 Microsoft Visual Studio 将新的 Page 添加到项目时,Page 是同时使用标记和代码隐藏实现的,它包含必要的配置来创建此处所述的标记文件和代码隐藏文件之间的关联。 |
只要有 Page,就可以导航到该页。若要指定应用程序导航到的第一个 Page,需要配置起始 Page。
配置起始页
XBAP 要求在浏览器中承载一定量的应用程序基础结构。在 WPF 中,Application 类是建立所需应用程序基础结构的应用程序定义的一部分(请参见应用程序管理概述)。
应用程序定义通常是同时使用标记和代码隐藏实现的,其中标记文件配置为 MSBuild ApplicationDefinition 项。下面是某个 XBAP 的应用程序定义。
XBAP 可使用其应用程序定义来指定起始 Page,该页是在 XBAP 启动时自动加载的 Page。指定方法是将 StartupUri 属性设置为所需 Page 的 统一资源标识符 (URI)。
可以在标记中以声明方式设置 StartupUri,如下面的示例所示。
<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" />
此例中,StartupUri 属性设置为标识 HomePage.xaml 的相对 pack URI。当 XBAP 启动时,将自动导航到 HomePage.xaml 并显示该文件。下图对此进行演示,图中演示一个从 Web 服务器启动的 XBAP。
.png)
配置宿主窗口的标题、宽度和高度
从上图中,您可能已注意到,浏览器和选项卡面板的标题都是 XBAP 的 URI。除了冗长之外,该标题既没有吸引力,又没有什么意义。因此,Page 提供了一种方法,可以通过设置 WindowTitle 属性来更改标题。此外,通过设置 WindowWidth 和 WindowHeight 还可以分别设置浏览器窗口的宽度和高度。
在标记中可以以声明方式设置 WindowTitle、WindowWidth 和 WindowHeight,如下面的示例所示。
<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>
下图演示了结果。
.png)
超链接导航
典型的 XBAP 由多个页组成。从一页导航到另一页的最简单方法是使用 Hyperlink。通过下面的标记中所示的 Hyperlink 元素,可以以声明方式将 Hyperlink 添加到 Page。
<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>
Hyperlink 元素需要以下组成部分:
下图演示一个 XBAP,它有一个含 Hyperlink 的 Page。
.png)
如您所料,如果单击 Hyperlink,XBAP 就会导航到 NavigateUri 属性所标识的 Page。此外,XBAP 将表示前一个 Page 的对应条目添加到 Internet Explorer 7 的“最新网页”列表中。下图对此进行演示。
.png)
除了支持从一个 Page 导航到另一页之外,Hyperlink 还支持片段导航。
片段导航
片段导航是导航到当前 Page 或另一个 Page 中的内容片段。在 WPF 中,内容片段是由命名元素包含的内容。命名元素是设置了 Name 属性的元素。下面的标记演示一个包含内容片段的命名 TextBlock 元素。
<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>
对于可导航到内容片段的 Hyperlink,NavigateUri 属性必须包含以下内容:
包含要导航到的内容片段的 Page 的 URI。
一个“#”字符。
Page 中包含内容片段的元素的名称。
片段 URI 的格式如下。
页 URI#元素名
下面演示一个 Hyperlink 示例,它配置为导航到内容片段。
<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>
有关片段导航的完整示例,请参见片段导航示例。
重要说明: |
|---|
只有在松散 XAML 页(以 Page 作为根元素的纯标记 XAML 文件)可以通过 HTTP 浏览时,才能导航到这些页中的片段。 但是,松散 XAML 页可以导航到自己的片段。 |
导航服务
利用导航服务的编程导航
如果导航是在标记中使用 Hyperlink 以声明方式实现的,则无需了解 NavigationService,这是因为 Hyperlink 代表您使用 NavigationService。这意味着,只要 Hyperlink 的直接或间接父级为导航宿主(请参见导航宿主),Hyperlink 就能够找到和使用导航宿主的导航服务来处理导航请求。
但是,某些情况下,需要直接使用 NavigationService,这些情况包括:
需要使用非默认构造函数启动 Page 页。
需要在导航到 Page 之前设置其属性。
需要导航到的 Page 只能在运行时确定。
在这些情况下,需要编写代码,以编程方式调用 NavigationService 对象的 Navigate 方法来发起导航。这样就需要获得对 NavigationService 的引用。
获取对 NavigationService 的引用
对页对象的编程导航
下面的示例演示如何使用 NavigationService 以编程方式导航到 Page。由于要导航到的 Page 只能使用单个非默认构造函数来实例化,因此需要编程导航。下面的标记和代码演示具有非默认构造函数的 Page。
<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;
}
}
}
下面的标记和代码演示导航到具有非默认构造函数的 Page 的 Page。
<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);
}
}
}
单击此 Page 上的 Hyperlink 时,将使用非默认构造函数实例化要导航到的 Page,然后调用 NavigationService..::.Navigate 方法,这样就发起了导航。Navigate 接受对 NavigationService 将要导航到的对象的引用,而不是 pack URI。
利用 Pack URI 的编程导航
如果需要以编程方式构造 pack URI(例如,如果只能在运行时确定 pack URI),可以使用 NavigationService..::.Navigate 方法。下面的示例对此进行演示。
<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);
}
}
}
刷新当前页
如果 Page 的 pack URI 与存储在 NavigationService..::.Source 属性中的 pack URI 相同,则不会下载该页。若要强制 WPF 再次下载当前页,可以调用 NavigationService..::.Refresh 方法,如下例所示。
<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();
}
}
}
导航生存期
正如您看到的那样,发起导航的方式有很多。发起导航时,或正在进行导航时,都可以使用 NavigationService 实现的下列事件来跟踪和影响导航:
导航事件的引发顺序如下图所示。
.png)
通常,Page 与这些事件无关。应用程序与这些事件的关系可能更大,因此,Application 类也会引发这些事件:
每当 NavigationService 引发事件时,Application 类都会引发对应的事件。Frame 和 NavigationWindow 提供了相同的事件以检测各自范围中的导航。
某些情况下,Page 可能需要这些事件。例如,Page 可能处理 NavigationService..::.Navigating 事件以确定是否取消从自己发出的导航。下面的示例对此进行演示。
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.CancelNavigationPage">
<Button Click="button_Click">Navigate to Another Page</Button>
</Page>
using System; // Uri, UriKind
using System.Windows; // RoutedEventArgs, MessageBox, MessageBoxResult
using System.Windows.Controls; // Page
using System.Windows.Navigation; // NavigationService, NavigatingCancelEventArgs
namespace SDKSample
{
public partial class CancelNavigationPage : Page
{
public CancelNavigationPage()
{
InitializeComponent();
// Can only access the NavigationService when the page has been loaded
this.Loaded += new RoutedEventHandler(CancelNavigationPage_Loaded);
this.Unloaded += new RoutedEventHandler(CancelNavigationPage_Unloaded);
}
void button_Click(object sender, RoutedEventArgs e)
{
// Force WPF to download this page again
this.NavigationService.Navigate(new Uri("AnotherPage.xaml", UriKind.Relative));
}
void CancelNavigationPage_Loaded(object sender, RoutedEventArgs e)
{
this.NavigationService.Navigating += new NavigatingCancelEventHandler(NavigationService_Navigating);
}
void CancelNavigationPage_Unloaded(object sender, RoutedEventArgs e)
{
this.NavigationService.Navigating -= new NavigatingCancelEventHandler(NavigationService_Navigating);
}
void NavigationService_Navigating(object sender, NavigatingCancelEventArgs e)
{
// Does the user really want to navigate to another page?
MessageBoxResult result;
result = MessageBox.Show("Do you want to leave this page?", "Navigation Request", MessageBoxButton.YesNo);
// If the user doesn't want to navigate away, cancel the navigation
if (result == MessageBoxResult.No) e.Cancel = true;
}
}
}
如上例所示,如果针对 Page 中的某个导航事件注册了相应的处理程序,则还必须注销该事件处理程序。否则,对于 WPF 导航使用日记记住 Page 导航来说,可能会产生副作用。
利用日记记住导航
WPF 使用两个堆栈来记住导航过的页:一个后退堆栈和一个前进堆栈。从当前 Page 导航到新 Page,或者前进到现有 Page 时,当前 Page 将添加到后退堆栈中。从当前 Page 导航到上一 Page 时,当前 Page 将添加到前进堆栈中。后退堆栈、前进堆栈和用于管理它们的功能统称为日记。后退堆栈和前进堆栈中的每一项都是 JournalEntry 类的实例,称为“日记条目”。
从 Internet Explorer 7 中导航日记
从概念上讲,日记的操作与 Internet Explorer 7 中的“返回”和“前进”按钮一样。下图对此进行演示。
.png)
对于 Internet Explorer 7 承载的 XBAP,WPF 将日记集成到 Internet Explorer 7 的导航 UI 中。这样,用户可以使用 Internet Explorer 7 中的“后退”、“前进”和“最新网页”按钮在 XBAP 的页中导航。Microsoft Internet Explorer 6 中没有像 Internet Explorer 7 那样集成日记。而是由 WPF 呈现一个替代导航 UI。
重要说明: |
|---|
在 Internet Explorer 7 中,当用户导航至其他地方,然后后退到 XBAP 时,只有那些未保持活动的页的对应日记条目才保留在日记中。有关如何使页保持活动的讨论,请参见本主题稍后部分的页生存期和日记。 |
默认情况下,出现在 Internet Explorer 7 的“最新网页”列表中的每一个 Page 的对应文本是该 Page 的 URI。很多情况下,这对用户没有多大意义。您可以使用以下选项之一来更改该文本:
附加的 JournalEntry.Name 属性值。
Page.Title 属性值。
当前 Page 的 Page.WindowTitle 属性值和 URI。
当前 Page 的 URI。(默认值)。
这些选项的列出顺序与用于查找文本的优先顺序一致。例如,如果设置了 JournalEntry.Name,则忽略其他值。
下面的示例使用 Page.Title 属性来更改日记条目的显示文本。
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.PageWithTitle"
Title="This is the title of the journal entry for this page.">
...
</Page>
using System.Windows.Controls; // Page
namespace SDKSample
{
public partial class PageWithTitle : Page
{
...
}
}
使用 WPF 导航日记
用户可以使用 Internet Explorer 7 中的“后退”、“前进”和“最新网页”来导航日记,也可以使用 WPF 提供的声明机制和编程机制来导航日记。这样做的一个原因是在页中提供自定义导航 UI。
使用 NavigationCommands 公开的导航命令可以以声明方式添加日记导航支持。下面的示例演示如何使用 BrowseBack 导航命令。
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.NavigationCommandsPage">
...
<Hyperlink Command="NavigationCommands.BrowseBack">Back</Hyperlink>
...
</Page>
使用 NavigationService 类的以下成员之一可以以编程方式导航日记:
此外,也可以以编程方式操作日记,本主题后面的利用导航历史记录保留内容状态将对此进行讨论。
页生存期和日记
请考虑这样一个 XBAP,它具有多个包含丰富内容的页,这些内容包括图形、动画和媒体。这类页面可能占用大量内存,如果使用视频和音频媒体,就更是如此。如果日记记住导航过的页,这样的 XBAP 可能很快耗尽大量内存。
因此,日记的默认行为是在每个日记条目中存储 Page 元数据,而不是存储对 Page 对象的引用。在导航到日记条目时,其 Page 元数据用于创建指定 Page 的新实例。因此,导航过的每一个 Page 都有如下图所示的生存期。
.png)
虽然使用默认日记行为可以减少内存消耗,但是可能导致逐页呈现性能降低;重新实例化 Page 可能消耗大量时间,尤其是在有大量内容的情况下。如果需要在日记中保留 Page 实例,可以采用两种技术来实现。第一种是,以编程方式调用 NavigationService..::.Navigate 方法导航到 Page 对象。
第二种是,通过将 KeepAlive 属性设置为 true(默认为 false),指定 WPF 在日记中保留 Page 的实例。如下例所示,可以在标记中以声明方式设置 KeepAlive。
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.KeepAlivePage"
KeepAlive="True">
An instance of this page is stored in the journal.
</Page>
活动状态的 Page 的生存期与非活动状态的页稍有不同。在首次导航到活动状态的 Page 时,它的实例化方式与非活动状态的 Page 相同。但是,由于 Page 的实例已保留在日记中,因此,只要它还在日记中,就不会再次实例化。因此,如果 Page 具有需要在每次导航到 Page 时都调用的初始化逻辑,就应将该逻辑从构造函数移到 Loaded 事件的处理程序中。如下图所示,每当导航到 Page 和从其导航到别处时,仍然会分别引发 Loaded 和 Unloaded 事件。
.png)
如果 Page 不处于活动状态,不应执行以下任何操作:
存储对该页或其任何部分的引用。
不是由该页实现的事件注册事件处理程序。
如果执行上面的任一操作,则会创建强制 Page 保留在内存中的引用,即使该页从日记中移除后,它仍保留在内存中。
通常,应首选默认 Page 行为,即不使 Page 保持为活动状态。不过,这涉及到将在下一节中讨论的状态问题。
利用导航历史记录保留内容状态
Cookie
WPF 应用程序存储数据的另一种方法是通过 Cookie,Cookie 是使用 SetCookie 和 GetCookie 方法创建、更新和删除的。可在 WPF 中创建的 Cookie 与其他类型的 Web 应用程序所使用的 Cookie 没有区别;Cookie 是由应用程序存储在客户端计算机上的任意数据片段,这些数据是在应用程序会话期间,或者跨应用程序会话存储的。Cookie 数据通常采用如下格式的名称/值对形式。
名称=值
当数据随 Uri(应为其设置 Cookie 的位置)一起传给 SetCookie 时,在内存中就会创建 Cookie,它仅在当前应用程序会话的持续时间内可用。此类型的 Cookie 称为“会话 Cookie”。
若要跨应用程序会话存储 Cookie,必须使用下面的格式将到期日期添加到 Cookie。
名称=值; expires=DAY, DD-MMM-YYYY HH:MM:SS GMT
带有到期日期的 Cookie 将一直存储在当前安装的 Windows 的 Temporary Internet Files 文件夹中,直到该 Cookie 到期。这种 Cookie 称为“永久性 Cookie”,这是因为它跨应用程序会话存在。
调用 GetCookie 方法,传入通过 SetCookie 方法设置 Cookie 的位置的 Uri,即可检索会话 Cookie 和永久性 Cookie。
WPF 从以下几个方面支持 Cookie:
WPF 独立应用程序和 XBAP 都可以创建和管理 Cookie。
由 XBAP 创建的 Cookie 可以从浏览器进行访问。
同一个域的 XBAP 可以创建和共享 Cookie。
同一个域的 XBAP 和 HTML 页可以创建和共享 Cookie。
当 XBAP 和松散 XAML 页发出 Web 请求时,将调度 Cookie。
顶级 XBAP 和承载在 IFRAME 中的 XBAP 可以访问 Cookie。
WPF 的 Cookie 支持对于所有受支持的浏览器(Internet Explorer 7、Microsoft Internet Explorer 6 和 Firefox 2.0+)都是相同的。
在 Microsoft Internet Explorer 6 和 Internet Explorer 7 中,WPF 符合与 Cookie 有关的 P3P 策略,尤其是对于第一方和第三方 XBAP。
结构化导航
至此,您已经了解了最有可能用于构建带有可导航内容的应用程序的全部导航服务。这些服务是以 XBAP 为例讨论的,但它们并不局限于 XBAP。现代操作系统和 Windows 应用程序利用当今用户的浏览器体验,将浏览器样式导航并入独立应用程序中。常见的示例包括:
若要将浏览器样式导航并入独立应用程序,可以使用 NavigationWindow 类。NavigationWindow 派生自 Window,并且将其扩展为支持 XBAP 所提供的导航方式。可以将 NavigationWindow 用作独立应用程序的主窗口或者对话框之类的辅助窗口。
若要实现 NavigationWindow,与 WPF 中的大多数顶级类(Window、Page 等等)一样,可以使用标记和代码隐藏的组合。下面的示例对此进行演示。
<NavigationWindow
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.MainWindow"
Source="HomePage.xaml"/>
using System.Windows.Navigation; // NavigationWindow
namespace SDKSample
{
public partial class MainWindow : NavigationWindow
{
public MainWindow()
{
InitializeComponent();
}
}
}
这段代码创建一个 NavigationWindow,当 NavigationWindow 打开时,它自动导航到 Page (HomePage.xaml)。如果该 NavigationWindow 为主应用程序窗口,则可以使用 StartupUri 属性启动它。下面的标记对此进行演示。
<Application
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
StartupUri="MainWindow.xaml" />
下图演示作为独立应用程序主窗口的 NavigationWindow。
.png)
从图中可以看到,尽管在上例的 NavigationWindow 实现代码中未设置标题,但 NavigationWindow 仍然有一个标题。该标题是使用 WindowTitle 属性设置的,如下面的代码所示。
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Title="Home Page"
WindowTitle="NavigationWindow">
...
</Page>
设置 WindowWidth 和 WindowHeight 属性也会影响 NavigationWindow。
通常,如果需要自定义 NavigationWindow 的行为或外观,则可以实现自己的 NavigationWindow。如果两者都不需要自定义,则可以使用快捷的方法。如果指定 Page 的 pack URI 作为独立应用程序的 StartupUri,则 Application 将自动创建一个 NavigationWindow 来承载 Page。下面的标记演示如何实现。
<Application
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
StartupUri="HomePage.xaml" />
如果希望对话框之类的辅助应用程序窗口作为 NavigationWindow,则可以使用下例中的代码打开该窗口。
// Open a navigation window as a dialog box
NavigationWindowDialogBox dlg = new NavigationWindowDialogBox();
dlg.Source = new Uri("HomePage.xaml", UriKind.Relative);
dlg.Owner = this;
dlg.ShowDialog();
下图演示了结果。
.png)
正如您看到的那样,NavigationWindow 显示 Internet Explorer 7 样式的“后退”和“前进”按钮,这两个按钮允许用户导航访问日记。这些按钮提供了相同的用户体验,如下图所示。
.png)
如果页提供了自己的日记导航支持和用户界面,则通过将 ShowsNavigationUI 属性的值设置为 false 可以隐藏 NavigationWindow 所显示的“后退”和“前进”按钮。或者,使用 WPF 中的自定义支持也可以替换 NavigationWindow 自身的 UI。有关如何执行此操作的示例,请参见自定义导航窗口用户界面示例。
浏览器和 NavigationWindow 都是承载可导航内容的窗口。某些情况下,应用程序含有的内容不需要由整个窗口承载。而应由其他内容承载这些内容。使用 Frame 类可以将可导航内容插入其他内容。Frame 提供了与 NavigationWindow 和 XBAP 相同的支持。
下面的示例演示如何使用 Frame 元素以声明方式将 Frame 添加到 Page。
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
WindowTitle="Page that Hosts a Frame"
WindowWidth="250"
WindowHeight="250">
...
<Frame Source="FramePage1.xaml" />
...
</Page>
这段标记将 Frame 元素的 Source 属性设置为 Frame 最初应导航到的 Page 的 pack URI。下图演示一个 XBAP,它具有一个 Page,该页有一个在多页之间导航的 Frame。
.png)
您并非只能在 Page 的内容中使用 Frame。在 Window 的内容中承载 Frame 也很常见。有关示例,请参见窗口中的框架导航示例。
默认情况下,只有缺少其他日记时,Frame 才使用自己的日记。如果 Frame 是承载在 NavigationWindow 或 XBAP 中的内容的一部分,则 Frame 将使用属于 NavigationWindow 或 XBAP 的日记。不过,有时 Frame 可能需要负责自己的日记。这样做的一个原因是允许在 Frame 所承载的页中进行日记导航。下图对此进行演示。
.png)
本例中,通过将 Frame 的 JournalOwnership 属性设置为 OwnsJournal 可以将 Frame 配置为使用自己的日记。下面的标记对此进行演示。
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
WindowTitle="Page that Hosts a Frame"
WindowWidth="250"
WindowHeight="250">
...
<Frame Source="FramePage1.xaml" JournalOwnership="OwnsJournal" />
...
</Page>
下图演示在使用自己日记的 Frame 中进行导航的效果。
.png)
请注意,日记条目是由 Frame 中的导航 UI 而不是 Internet Explorer 7 显示的。
如果用户体验要求 Frame 提供自己的日记,而不显示导航 UI,则通过将 NavigationUIVisibility 设置为 Hidden 可以隐藏导航 UI。下面的标记对此进行演示。
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
WindowTitle="Page that Hosts a Frame"
WindowWidth="250"
WindowHeight="250">
...
<Frame
Source="FramePage1.xaml"
JournalOwnership="OwnsJournal"
NavigationUIVisibility="Hidden" />
...
</Page>
本主题中,已使用 Page 和 pack XBAP 演示 WPF 的各种导航功能。但是,编译到应用程序中的 Page 并不是可作为导航目标的内容的唯一类型,并且 pack XBAP 也不是用来标识内容的唯一方法。
如本节所示,还可以导航到松散 XAML 文件、HTML 文件和对象。
导航到松散 XAML 文件
松散 XAML 文件是具有以下特点的文件:
只包含 XAML(即,无代码)。
具有适当的命名空间声明。
具有 .xaml 文件扩展名。
例如,考虑作为松散 XAML 文件 Person.xaml 存储的以下内容。
<!-- Person.xaml -->
<TextBlock xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<TextBlock FontWeight="Bold">Name:</TextBlock>
<TextBlock>Nancy Davolio</TextBlock>
<LineBreak />
<TextBlock FontWeight="Bold">Favorite Color:</TextBlock>
<TextBlock>Yellow</TextBlock>
</TextBlock>
双击该文件时,浏览器将打开,然后导航到这些内容并显示。下图对此进行演示。
.png)
可以显示以下位置的松散 XAML 文件:
松散 XAML 文件可添加到浏览器收藏夹中,或作为浏览器的主页。
与松散 XAML 有关的一个限制是,只能承载对于以部分信任运行来说安全的内容。例如,Window 不可为松散 XAML 文件的根元素。有关更多信息,请参见 Windows Presentation Foundation 部分信任安全性。
使用框架导航到 HTML 文件
您可能已想到,还可以导航到 HTML。您只需提供一个使用 http 方案的 URI 即可。例如,下面的 XAML 演示一个导航到 HTML 页的 Frame。
<Frame Source="http://www.microsoft.com/default.aspx" />
导航到 HTML 需要特殊的权限。例如,不能从在 Internet 区域部分信任安全沙箱中运行的 XBAP 进行导航。有关更多信息,请参见 Windows Presentation Foundation 部分信任安全性。
使用 WebBrowser 控件导航到 HTML 文件
导航到自定义对象
如果有存储为自定义对象的数据,那么显示该数据的一种方法是创建一个 Page,并且其内容绑定到这些对象(请参见数据绑定概述)。如果不希望只为显示这些对象而造成创建整页的开销,可以改为直接导航到这些对象。
考虑下面代码中实现的 Person 类。
为了导航到该对象,可以调用 NavigationWindow..::.Navigate 方法,如下面的代码所示。
下图演示了结果。
.png)
从图中可以看到,显示的内容没有用处。实际上,显示的值是“Person”对象的 ToString 方法的返回值;默认情况下,这是 WPF 唯一可用来表示对象的值。重写 ToString 方法可以返回更有意义的信息,尽管该信息仍只是字符串值。可使用的一种利用 WPF 表示功能的方法是使用数据模板。可以实现一个数据模板,WPF 可将其与某种特定类型的对象相关联。下面的代码演示 Person 对象的数据模板。
在这里,数据模板使用 DataType 属性中的 x:Type 标记扩展与 Person 类型关联。该数据模板随后将 TextBlock 元素(请参见 TextBlock)绑定到 Person 类的属性。下图演示 Person 对象更新后的外观。
.png)
此方法的一个优点是一致性,这是因为能重用数据模板,从而在应用程序的任何位置一致地显示对象。
有关完整示例,请参见 导航至自定义对象示例。
有关数据模板的更多信息,请参见数据模板化概述。