导航概述
Windows Presentation Foundation (WPF) 支持可用于两种类型应用程序的浏览器样式的导航功能:独立应用程序和 XAML browser applications (XBAPs)。为了将用于导航的内容打包,WPF 提供了 Page 类。 可以通过使用 Hyperlink 以声明方式或通过使用 NavigationService 以编程方式从一个 Page 导航到另一个。WPF 使用日志记忆已从其进行导航的页面以及导航回到这些页面。
Page、Hyperlink、NavigationService 和日记构成了 WPF 所提供的导航支持的核心。 在介绍高级导航支持(包括导航到宽松 Extensible Application Markup Language (XAML) 文件、HTML 文件和对象)之前,本概述先详细讨论上面提到的这些功能。
注意 |
---|
在本主题中,术语“浏览器”仅指可承载 WPF 应用程序的浏览器,当前包括 Microsoft Internet Explorer 和 Firefox。在特定 WPF 功能仅由特定浏览器支持的场合下,将说明浏览器的版本。 |
本主题包括下列各节。
- WPF 应用程序中的导航
- NavigationWindow 类
- Frame 类
- 导航宿主
- 导航到非 XAML 页内容
- 安全性
- 相关主题
WPF 应用程序中的导航
本主题提供 WPF 中的关键导航功能的概述。 虽然在本主题中,这些功能是以 XBAP 为例介绍的,但它们既可用于 XBAPs,也可用于独立应用程序。
注意 |
---|
本主题不讨论如何生成和部署 XBAPs。有关 XBAPs 的更多信息,请参见 WPF XAML 浏览器应用程序概述。 |
本节说明并演示导航的以下方面:
实现页
配置起始页
配置宿主窗口的标题、宽度和高度
超链接导航
片段导航
导航服务
利用导航服务的编程导航
导航生存期
利用日记记住导航
页生存期和日记
利用导航历史记录保留内容状态
Cookie
结构化导航
实现页
在 WPF 中,可以导航到多种内容,包括 .NET Framework 对象、自定义对象、枚举值、用户控件、XAML 文件和 HTML 文件。 但是,打包内容的最常用和最方便的方式还是使用 Page。 另外,Page 还实现了特定于导航的功能,以增强其外观并简化开发过程。
利用 Page,可以使用类似于下面的标记来以声明方式实现 XAML 内容的可导航页。
<Page xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation" />
在 XAML 标记中实现的 Page 将 Page 作为其根元素,并且需要 WPF XML 命名空间声明。 Page 元素包含要导航到并显示的内容。 通过设置 Page.Content 属性元素可以添加内容,如下面的标记所示。
<Page xmlns="https://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="https://schemas.microsoft.com/winfx/2006/xaml/presentation">
<!-- Page Content -->
Hello, Page!
</Page>
此例中,以 Page 元素的子元素自动设置了 Page.Content。 有关更多信息,请参见 WPF 内容模型。
纯标记 Page 对于显示内容很有用。 但是,Page 也可显示允许用户与页进行交互的控件,它还可通过处理事件和调用应用程序逻辑来响应用户交互。 交互式 Page 是使用标记和代码隐藏的组合实现的,如下面的示例所示。
<Page
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.HomePage">
Hello, from the XBAP HomePage!
</Page>
Imports System.Windows.Controls ' Page
Namespace SDKSample
Partial Public Class HomePage
Inherits Page
Public Sub New()
InitializeComponent()
End Sub
End Class
End Namespace
using System.Windows.Controls; // Page
namespace SDKSample
{
public partial class HomePage : Page
{
public HomePage()
{
InitializeComponent();
}
}
}
为了使标记文件和代码隐藏文件配合工作,需要以下配置:
在标记中,Page 元素必须包含 x:Class 特性。 生成应用程序时,标记文件中如果存在 x:Class,则 Microsoft build engine (MSBuild) 将创建一个从 Page 派生的 partial 类,并且该类的名称是由 x:Class 特性指定的。 这要求添加 XAML 架构的 XML 命名空间声明 (xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml")。 生成的 partial 类实现 InitializeComponent,调用该方法可注册事件并设置在标记中实现的属性。
在代码隐藏中,该类必须是 partial 类,必须具有标记中的 x:Class 特性所指定的名称,且必须派生自 Page。 这样,代码隐藏文件就与应用程序生成时为标记文件生成的 partial 类相关联(请参见生成 WPF 应用程序 (WPF))。
在代码隐藏中,Page 类必须实现调用 InitializeComponent 方法的构造函数。 标记文件的已生成的 partial 类实现 InitializeComponent,以便注册事件和设置在标记中定义的属性。
注意 |
---|
在使用 Microsoft Visual Studio 将新的 Page 添加到项目时,Page 是同时使用标记和代码隐藏实现的,它包含必要的配置来创建此处所述的标记文件和代码隐藏文件之间的关联。 |
只要有 Page,就可以导航到该页。 若要指定应用程序导航到的第一个 Page,需要配置起始 Page。
配置起始页
XBAPs 要求在浏览器中承载一定量的应用程序基础结构。 在 WPF 中,Application 类是建立所需应用程序基础结构的应用程序定义的一部分(请参见应用程序管理概述)。
应用程序定义通常是同时使用标记和代码隐藏实现的,其中标记文件配置为 MSBuild ApplicationDefinition 项。下面是某个 XBAP 的应用程序定义。
<Application
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.App" />
Imports System.Windows ' Application
Namespace SDKSample
Partial Public Class App
Inherits Application
End Class
End Namespace
using System.Windows; // Application
namespace SDKSample
{
public partial class App : Application { }
}
XBAP 可使用其应用程序定义指定起始 Page(它是启动 XBAP 时自动加载的 Page)。 指定方法是将 StartupUri 属性设置为所需的 Page 的 uniform resource identifier (URI)。
注意 |
---|
大多数情况下,Page 都编译到应用程序中,或者与应用程序一起部署。在这些情况下,标识 Page 的 URI 为 pack URI,这是符合 pack 方案的 URI。Pack URIs 将在 WPF 中的 Pack URI 中进一步讨论。此外,还可以使用 http 方案导航到内容,下面将讨论这个问题。 |
可以在标记中以声明方式设置 StartupUri,如下面的示例所示。
<Application
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.App"
StartupUri="PageWithHyperlink.xaml" />
此例中,StartupUri 特性设置为标识 HomePage.xaml 的相对 pack URI。 当 XBAP 启动时,将自动导航到 HomePage.xaml 并显示该文件。 下图演示了这一点,图中显示从 Web 服务器启动的一个 XBAP。
注意 |
---|
有关 XBAPs 的开发和部署的更多信息,请参见 WPF XAML 浏览器应用程序概述和部署 WPF 应用程序 (WPF)。 |
配置宿主窗口的标题、宽度和高度
从上图中,您可能已注意到,浏览器和选项卡面板的标题都是 XBAP 的 URI。 除了冗长之外,该标题既没有吸引力,又没有什么意义。 因此,Page 提供了一种方法,可以通过设置 WindowTitle 属性来更改标题。 此外,通过设置 WindowWidth 和 WindowHeight 还可以分别设置浏览器窗口的宽度和高度。
在标记中可以以声明方式设置 WindowTitle、WindowWidth 和 WindowHeight,如下面的示例所示。
<Page
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.HomePage"
WindowTitle="Page Title"
WindowWidth="500"
WindowHeight="200">
Hello, from the XBAP HomePage!
</Page>
下图演示了结果。
超链接导航
典型的 XBAP 由多个页组成。 从一页导航到另一页最简单的方法是使用 Hyperlink。 通过下面的标记中所示的 Hyperlink 元素,可以以声明方式将 Hyperlink 添加到 Page。
<Page
xmlns="https://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 元素需要以下组成部分:
要导航到的 Page 的 pack URI,它由 NavigateUri 特性指定。
通过用户单击即可启动导航的内容,如文本和图像(有关 Hyperlink 元素可包含的内容,请参见 Hyperlink)。
下图演示一个 XBAP,它有一个含 Hyperlink 的 Page。
如您所料,如果单击 Hyperlink,XBAP 就会导航到 NavigateUri 特性所标识的 Page。 此外,XBAP 将表示前一个 Page 的对应条目添加到 Internet Explorer 的“最新网页”列表中。 下图对此进行演示。
除了支持从一个 Page 导航到另一页之外,Hyperlink 还支持片段导航。
片段导航
片段导航是导航到当前 Page 或另一个 Page 中的内容片段。 在 WPF 中,内容片段是由命名元素包含的内容。 命名元素是设置了 Name 特性的元素。 下面的标记演示一个包含内容片段的命名 TextBlock 元素。
<Page
xmlns="https://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 特性必须包含以下内容:
片段 URI 的格式如下。
页 URI#元素名称
下面演示一个 Hyperlink 示例,它配置为导航到内容片段。
<Page
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
WindowTitle="Page That Navigates To Fragment" >
...
<Hyperlink NavigateUri="PageWithFragments.xaml#Fragment1">
Navigate To pack Fragment
</Hyperlink>
...
</Page>
注意 |
---|
本节介绍 WPF 中片段导航的默认实现。WPF 也允许您实现自己的在某种程度上需要处理 NavigationService.FragmentNavigation 事件的片段导航方案。 |
重要事项 |
---|
只有在松散 XAML 页(以 Page 作为根元素的纯标记 XAML 文件)可以通过 HTTP 浏览时,才能导航到这些页中的片段。 但是,松散 XAML 页可以导航到自己的片段。 |
导航服务
虽然 Hyperlink 允许用户发起转向特定 Page 的导航,但是定位和下载该页的工作仍由 NavigationService 类执行。 从根本上说,NavigationService 提供了代表客户端代码(如 Hyperlink)处理导航请求的功能。 此外,对于跟踪和影响导航请求,NavigationService 实现了更高级别的支持。
单击 Hyperlink 时,WPF 将调用 NavigationService.Navigate 来定位和下载位于指定 pack URI 的 Page。 下载的 Page 将转换为对象树,其根对象为下载的 Page 的实例。 对根 Page 对象的引用存储在 NavigationService.Content 属性中。 导航过的内容的 pack URI 存储在 NavigationService.Source 属性中,而 NavigationService.CurrentSource 存储所导航的上一页的 pack URI。
注意 |
---|
一个 WPF 应用程序可以有多个当前活动的 NavigationService。有关更多信息,请参见本主题后面的导航宿主。 |
利用导航服务的编程导航
如果导航是在标记中使用 Hyperlink 以声明方式实现的,则无需了解 NavigationService,这是因为 Hyperlink 代表您使用 NavigationService。 这意味着,只要 Hyperlink 的直接或间接父级为导航宿主(请参见导航宿主),Hyperlink 就能够找到和使用导航宿主的导航服务来处理导航请求。
但是,某些情况下,需要直接使用 NavigationService,这些情况包括:
在这些情况下,需要编写代码,以编程方式调用 NavigationService 对象的 Navigate 方法来发起导航。 这样就需要获得对 NavigationService 的引用。
获取对 NavigationService 的引用
由于导航宿主一节中所提及的原因,WPF 应用程序可以有多个 NavigationService。 这意味着,代码需要一种方法来找到 NavigationService,通常就是导航到当前 Page 的 NavigationService。通过调用 static NavigationService.GetNavigationService 方法,您可以获取对 NavigationService 的引用。 若要获得导航到特定 Page 的 NavigationService,请将对 Page 的引用传递为 GetNavigationService 方法的参数。 下面的代码演示如何获取当前 Page 的 NavigationService。
' Get a reference to the NavigationService that navigated to this Page
Dim ns As NavigationService = NavigationService.GetNavigationService(Me)
using System.Windows.Navigation; // NavigationServce
...
// Get a reference to the NavigationService that navigated to this Page
NavigationService ns = NavigationService.GetNavigationService(this);
Page 实现了 NavigationService 属性,作为查找 Page 的 NavigationService 的快捷方法。 下面的示例对此进行演示。
' Get a reference to the NavigationService that navigated to this Page
Dim ns As NavigationService = Me.NavigationService
using System.Windows.Navigation; // NavigationServce
...
// Get a reference to the NavigationService that navigated to this Page
NavigationService ns = this.NavigationService;
注意 |
---|
当 Page 引发 Loaded 事件时,Page 只能获得一个对其 NavigationService 的引用。 |
对页对象的编程导航
下面的示例演示如何使用 NavigationService 以编程方式导航到 Page。 由于要导航到的 Page 只能使用单个非默认构造函数来实例化,因此需要编程导航。 下面的标记和代码演示具有非默认构造函数的 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">
<!-- Content goes here -->
</Page>
Namespace SDKSample
Partial Public Class PageWithNonDefaultConstructor
Inherits Page
Public Sub New(ByVal message As String)
InitializeComponent()
Me.Content = message
End Sub
End Class
End Namespace
using System.Windows.Controls; // Page
namespace SDKSample
{
public partial class PageWithNonDefaultConstructor : Page
{
public PageWithNonDefaultConstructor(string message)
{
InitializeComponent();
this.Content = message;
}
}
}
下面的标记和代码演示导航到具有非默认构造函数的 Page 的 Page。
<Page
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.NSNavigationPage">
<Hyperlink Click="hyperlink_Click">
Navigate to Page with Non-Default Constructor
</Hyperlink>
</Page>
Namespace SDKSample
Partial Public Class NSNavigationPage
Inherits Page
Public Sub New()
InitializeComponent()
End Sub
Private Sub hyperlink_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
' Instantiate the page to navigate to
Dim page As New PageWithNonDefaultConstructor("Hello!")
' Navigate to the page, using the NavigationService
Me.NavigationService.Navigate(page)
End Sub
End Class
End Namespace
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 后,通过使用非默认构造函数并调用 NavigationService.Navigate 方法将要导航到的 Page 实例化,从而启动导航。 Navigate 接受对 NavigationService 将导航到的对象的引用,而不接受 pack URI。
利用 Pack URI 的编程导航
如果需要以编程方式构造 pack URI(例如,如果只能在运行时确定 pack URI),可以使用 NavigationService.Navigate 方法。 下面的示例对此进行演示。
<Page
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.NSUriNavigationPage">
<Hyperlink Click="hyperlink_Click">Navigate to Page by Pack URI</Hyperlink>
</Page>
Namespace SDKSample
Partial Public Class NSUriNavigationPage
Inherits Page
Public Sub New()
InitializeComponent()
End Sub
Private Sub hyperlink_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
' Create a pack URI
Dim uri As New Uri("AnotherPage.xaml", UriKind.Relative)
' Get the navigation service that was used to
' navigate to this page, and navigate to
' AnotherPage.xaml
Me.NavigationService.Navigate(uri)
End Sub
End Class
End Namespace
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="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.NSRefreshNavigationPage">
<Hyperlink Click="hyperlink_Click">Refresh this page</Hyperlink>
</Page>
Namespace SDKSample
Partial Public Class NSRefreshNavigationPage
Inherits Page
...
Private Sub hyperlink_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
' Force WPF to download this page again
Me.NavigationService.Refresh()
End Sub
End Class
End Namespace
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 实现的下列事件来跟踪和影响导航:
Navigating. 在请求新导航时发生。 可用于取消导航。
NavigationProgress. 在下载过程中定期发生,以提供导航进度信息。
Navigated. 在已定位和下载页后发生。
NavigationStopped. 在导航停止(通过调用 StopLoading)时发生,或者在当前导航进行过程中请求新的导航时发生。
NavigationFailed. 在导航到所请求内容出错时发生。
LoadCompleted. 在导航到的页已加载、分析并已开始呈现时发生。
FragmentNavigation. 在开始导航到内容片段时发生,具体情况为:
如果所需片段在当前内容中,则立即发生。
如果所需片段在其他内容中,则在加载源内容之后发生。
导航事件的引发顺序如下图所示。
通常,Page 与这些事件无关。 应用程序与这些事件的关系可能更大,因此,Application 类也会引发这些事件:
每当 NavigationService 引发事件时,Application 类就会引发相应的事件。 Frame 和 NavigationWindow 提供相同的事件,以检测二者各自范围内的导航。
某些情况下,Page 可能需要这些事件。 例如,Page 可能处理 NavigationService.Navigating 事件以确定是否取消从自己发出的导航。 下面的示例对此进行演示。
<Page
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.CancelNavigationPage">
<Button Click="button_Click">Navigate to Another Page</Button>
</Page>
Namespace SDKSample
Partial Public Class CancelNavigationPage
Inherits Page
Public Sub New()
InitializeComponent()
' Can only access the NavigationService when the page has been loaded
AddHandler Loaded, AddressOf CancelNavigationPage_Loaded
AddHandler Unloaded, AddressOf CancelNavigationPage_Unloaded
End Sub
Private Sub button_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
' Force WPF to download this page again
Me.NavigationService.Navigate(New Uri("AnotherPage.xaml", UriKind.Relative))
End Sub
Private Sub CancelNavigationPage_Loaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
AddHandler NavigationService.Navigating, AddressOf NavigationService_Navigating
End Sub
Private Sub CancelNavigationPage_Unloaded(ByVal sender As Object, ByVal e As RoutedEventArgs)
RemoveHandler NavigationService.Navigating, AddressOf NavigationService_Navigating
End Sub
Private Sub NavigationService_Navigating(ByVal sender As Object, ByVal e As NavigatingCancelEventArgs)
' Does the user really want to navigate to another page?
Dim result As MessageBoxResult
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 Then
e.Cancel = True
End If
End Sub
End Class
End Namespace
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 中导航日记
从概念上讲,日记的操作与 Internet Explorer 中的**“返回”和“前进”**按钮一样。 下图对此进行演示。
对于 Internet Explorer 承载的 XBAPs,WPF 将日记集成到 Internet Explorer 的导航 UI 中。 这样,用户就可使用 Internet Explorer 中的**“后退”、“前进”和“最新网页”**按钮在 XBAP 的页中导航。日记没有通过其用于 Internet Explorer 7 或 Internet Explorer 8 的相同方式集成到 Microsoft Internet Explorer 6 中。 而是由 WPF 呈现一个替代导航 UI。
重要事项 |
---|
在 Internet Explorer 中,当用户导航至其他地方,然后后退到 XBAP 时,只有那些未保持活动的页的对应日记条目才保留在日记中。有关如何使页保持活动的讨论,请参见本主题后面的页生存期和日记。 |
默认情况下,出现在 Internet Explorer 的**“最新网页”**列表中的每一个 Page 的对应文本是该 Page 的 URI。 很多情况下,这对用户没有多大意义。 您可以使用以下选项之一来更改该文本:
附加的 JournalEntry.Name 特性值。
Page.Title 特性值。
当前 Page 的 Page.WindowTitle 特性值和 URI。
当前 Page 的 URI。 (默认值)
这些选项的列出顺序与用于查找文本的优先顺序一致。 例如,如果设置了 JournalEntry.Name,则忽略其他值。
下面的示例使用 Page.Title 特性来更改日记条目的显示文本。
<Page
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.PageWithTitle"
Title="This is the title of the journal entry for this page.">
...
</Page>
Namespace SDKSample
Partial Public Class PageWithTitle
Inherits Page
...
End Class
End Namespace
using System.Windows.Controls; // Page
namespace SDKSample
{
public partial class PageWithTitle : Page
{
...
}
}
使用 WPF 导航日记
用户可以使用 Internet Explorer 中的**“后退”、“前进”和“最新网页”**来导航日记,也可以使用 WPF 提供的声明机制和编程机制来导航日记。 这样做的一个原因是在页中提供自定义导航 UIs。
使用 NavigationCommands 公开的导航命令可以以声明方式添加日记导航支持。 下面的示例演示如何使用 BrowseBack 导航命令。
<Page
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.NavigationCommandsPage">
...
<Hyperlink Command="NavigationCommands.BrowseBack">Back</Hyperlink>
...
<Hyperlink Command="NavigationCommands.BrowseForward">Forward</Hyperlink>
...
</Page>
使用 NavigationService 类的以下成员之一可以以编程方式导航日记:
此外,也可以以编程方式操作日记,本主题后面的利用导航历史记录保留内容状态将对此进行讨论。
页生存期和日记
请考虑这样一个 XBAP,它具有多个包含丰富内容的页,这些内容包括图形、动画和媒体。 这类页面可能占用大量内存,使用视频和音频媒体时这种现象尤为明显。 如果日记“记忆”导航过的页,则此类 XBAP 可能很快就会明显消耗大量内存。
因此,日记的默认行为是在每个日记条目中存储 Page 元数据,而不是存储对 Page 对象的引用。 在导航到日记条目时,其 Page 元数据用于创建指定 Page 的新实例。 因此,导航过的每一个 Page 都有如下图所示的生存期。
虽然使用默认日记行为可以减少内存消耗,但是可能导致逐页呈现性能降低;重新实例化 Page 可能消耗大量时间,尤其是在有大量内容的情况下。 如果需要在日记中保留 Page 实例,可以采用两种技术来实现。 第一种是,调用 NavigationService.Navigate 方法,从而以编程方式导航到 Page 对象。
第二,通过将 KeepAlive 属性设置为 true(默认为 false),可以指定 WPF 在日记中保留 Page 的实例。 如下例所示,可以在标记中以声明方式设置 KeepAlive。
<Page
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://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 事件。
如果 Page 不处于活动状态,不应执行以下任何操作:
存储对该页或其任何部分的引用。
不是由该页实现的事件注册事件处理程序。
如果执行上面的任一操作,则会创建强制 Page 保留在内存中的引用,即使该页从日记中移除后,它仍保留在内存中。
通常,应首选默认 Page 行为,即不使 Page 保持为活动状态。 不过,这涉及到将在下一节中讨论的状态问题。
利用导航历史记录保留内容状态
如果 Page 不活动,并且它具有用于收集用户数据的控件,则当用户离开再返回 Page 时,数据会发生什么情况? 从用户体验的角度讲,用户希望看到自己此前输入的数据。 遗憾的是,因为每次导航都会创建 Page 的新实例,所以收集数据的控件将重新实例化,从而丢失数据。
所幸的是,日记为跨 Page 导航记忆数据(包括控件数据)提供了支持。 具体地说,每个 Page 的日记条目都用作关联的 Page 状态的临时容器。 下面的步骤概述在从 Page 导航到其他位置时是如何使用此支持的:
在使用日记导航回 Page 时,将执行以下步骤:
如果 Page 上使用了以下控件,WPF 会自动使用此支持:
如果 Page 使用这些控件,则在进行跨 Page 导航时,会记住输入这些控件的数据,如下图中的**“Favorite Color”(喜好颜色)**ListBox 所示。
如果 Page 含有上面列表中未列出的控件,或者状态存储在自定义对象中,则需要编写代码使日记能够记忆跨 Page 导航状态。
如果需要记忆少量跨 Page 导航状态,可以使用通过 FrameworkPropertyMetadata.Journal 元数据标志配置的依赖项属性(请参见 DependencyProperty)。
如果 Page 需要跨导航记住的状态包含较多数据,则将状态封装在单个类中,然后实现 IProvideCustomContentState 接口,可以减少代码量。
如果需要在单个 Page 的各状态之间导航,而又不离开 Page 本身,则可以使用 IProvideCustomContentState 和 NavigationService.AddBackEntry。
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 独立应用程序和 XBAPs 都可以创建和管理 Cookie。
由 XBAP 创建的 Cookie 可以从浏览器进行访问。
同一个域的 XBAPs 可以创建和共享 Cookie。
同一个域的 XBAPs 和 HTML 页可以创建和共享 Cookie。
当 XBAPs 和松散 XAML 页发出 Web 请求时,将调度 Cookie。
顶级 XBAPs 和承载在 IFRAME 中的 XBAPs 可以访问 Cookie。
WPF 中的 Cookie 支持对于所有支持的浏览器都相同。
在 Internet Explorer 中,WPF 符合与 Cookie 有关的 P3P 策略,尤其是对于第一方和第三方 XBAPs。
结构化导航
如果需要将数据从一个 Page 传到另一页,可以将数据作为参数传给 Page 的非默认构造函数。 注意,如果要使用此方法,必须使 Page 保持为活动状态;否则,当下次导航到该 Page 时,WPF 将使用默认构造函数重新实例化该 Page。
另一种办法是,Page 可实现用需要传入的数据设置的属性。 但是,当 Page 需要将数据传回导航到它的 Page 时,就比较麻烦。 问题是导航本质上不支持可保证在从 Page 离开之后将再返回到该页的机制。 导航实质上不支持调用/返回语义。 为了解决此问题,WPF 提供了 PageFunction<T> 类,使用该类可确保以可预知的结构化方式返回到 Page。 有关更多信息,请参见结构化导航概述。
NavigationWindow 类
至此,您已经了解了最有可能用于构建带有可导航内容的应用程序的全部导航服务。 这些服务是以 XBAPs 为例讨论的,但它们并不局限于 XBAPs。 现代操作系统和 Windows 应用程序利用当今用户的浏览器体验,将浏览器样式导航并入独立应用程序中。常见示例包括:
Word Thesaurus:导航访问单词选择。
文件资源管理器:导航访问文件和文件夹。
向导:将复杂任务拆分成多页,在这些页之间可以进行导航。 向导的一个示例是 Windows 组件向导,它处理添加和移除 Windows 功能的工作。
若要将浏览器样式导航并入独立应用程序,可以使用 NavigationWindow 类。 NavigationWindow 派生自 Window,并且将其扩展为同样支持 XBAPs 所提供的导航方式。可以将 NavigationWindow 用作独立应用程序的主窗口或者对话框之类的辅助窗口。
若要实现 NavigationWindow,与 WPF 中的大多数顶级类(Window、Page 等等)一样,请使用标记和代码隐藏的组合。 下面的示例对此进行演示。
<NavigationWindow
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.MainWindow"
Source="HomePage.xaml"/>
Namespace SDKSample
Partial Public Class MainWindow
Inherits NavigationWindow
Public Sub New()
InitializeComponent()
End Sub
End Class
End Namespace
using System.Windows.Navigation; // NavigationWindow
namespace SDKSample
{
public partial class MainWindow : NavigationWindow
{
public MainWindow()
{
InitializeComponent();
}
}
}
这段代码创建一个 NavigationWindow,当 NavigationWindow 打开时,它自动导航到 Page (HomePage.xaml)。 如果该 NavigationWindow 为主应用程序窗口,则可以使用 StartupUri 特性启动它。 下面的标记对此进行演示。
<Application
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
StartupUri="MainWindow.xaml" />
下图演示作为独立应用程序主窗口的 NavigationWindow。
从图中可以看到,尽管在上例的 NavigationWindow 实现代码中未设置标题,但 NavigationWindow 仍然有一个标题。 该标题是使用 WindowTitle 属性设置的,如下面的代码所示。
<Page
xmlns="https://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="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
StartupUri="HomePage.xaml" />
如果希望对话框之类的辅助应用程序窗口作为 NavigationWindow,则可以使用下例中的代码打开该窗口。
' Open a navigation window as a dialog box
Dim dlg As New NavigationWindowDialogBox()
dlg.Source = New Uri("HomePage.xaml", UriKind.Relative)
dlg.Owner = Me
dlg.ShowDialog()
// 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();
下图演示了结果。
正如您看到的那样,NavigationWindow 显示 Internet Explorer 样式的**“后退”和“前进”**按钮,这两个按钮允许用户导航访问日记。 这些按钮提供了相同的用户体验,如下图所示。
如果页提供了自己的日记导航支持和用户界面,则通过将 ShowsNavigationUI 属性的值设置为 false 可以隐藏 NavigationWindow 所显示的**“后退”和“前进”**按钮。
或者,使用 WPF 中的自定义支持也可以替换 NavigationWindow 自身的 UI。
Frame 类
浏览器和 NavigationWindow 都是承载可导航内容的窗口。 某些情况下,应用程序含有的内容不需要由整个窗口承载。 而应由其他内容承载这些内容。 使用 Frame 类可以将可导航内容插入其他内容。 Frame 提供与 NavigationWindow 和 XBAPs 相同的支持。
下面的示例演示如何使用 Frame 元素以声明方式将 Frame 添加到 Page。
<Page
xmlns="https://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。
您并非只能在 Page 的内容中使用 Frame。 在 Window 的内容中承载 Frame 也很常见。
默认情况下,只有缺少其他日记时,Frame 才使用自己的日记。 如果 Frame 是承载在 NavigationWindow 或 XBAP 中的内容的一部分,则 Frame 将使用属于 NavigationWindow 或 XBAP 的日记。不过,有时 Frame 可能需要负责自己的日记。 这样做的一个原因是允许在 Frame 所承载的页中进行日记导航。 下图对此进行演示。
本例中,通过将 Frame 的 JournalOwnership 属性设置为 OwnsJournal 可以将 Frame 配置为使用自己的日记。 下面的标记对此进行演示。
<Page
xmlns="https://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 中进行导航的效果。
请注意,日记条目是由 Frame 中的导航 UI 而不是 Internet Explorer 显示的。
注意 |
---|
如果 Frame 是 Window 中承载的内容的一部分,则 Frame 使用自己的日记,因此,会显示自己的导航 UI。 |
如果用户体验要求 Frame 提供自己的日记,而不显示导航 UI,则通过将 NavigationUIVisibility 设置为 Hidden 可以隐藏导航 UI。 下面的标记对此进行演示。
<Page
xmlns="https://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>
导航宿主
Frame 和 NavigationWindow 是称为导航宿主的类。 “导航宿主”是可以作为导航目标并显示内容的类。 为了实现这一点,每个导航宿主都使用自己 NavigationService 和日记。 导航宿主的基本构造如下图所示。
实质上,这样,NavigationWindow 和 Frame 就可以提供 XBAP 承载在浏览器中时所提供的导航支持。
除了使用 NavigationService 和日记外,导航宿主还实现了 NavigationService 所实现的成员。 下图对此进行演示。
这允许您直接对它们进行导航支持编程。 如果需要为承载在 Window 中的 Frame 提供自定义导航 UI,则可以考虑此方法。此外,这两种类型都实现了与导航相关的其他成员,包括 BackStack (NavigationWindow.BackStack, Frame.BackStack) 和 ForwardStack (NavigationWindow.ForwardStack, Frame.ForwardStack),它们可分别用来枚举后退堆栈和前进堆栈中的日记条目。
如前所述,一个应用程序中可存在多个日记。 下图演示可能出现此情况的示例。
导航到非 XAML 页内容
本主题中,已使用 Page 和 pack XBAPs 演示 WPF 的各种导航功能。 但是,编译到应用程序中的 Page 并不是可作为导航目标的内容的唯一类型,并且 pack XBAPs 也不是用来标识内容的唯一方法。
如本节所示,还可以导航到松散 XAML 文件、HTML 文件和对象。
导航到松散 XAML 文件
松散 XAML 文件是具有以下特点的文件:
只包含 XAML(即,无代码)。
具有适当的命名空间声明。
具有 .xaml 文件扩展名。
例如,考虑作为松散 XAML 文件 Person.xaml 存储的以下内容。
<!-- Person.xaml -->
<TextBlock xmlns="https://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>
双击该文件时,浏览器将打开,然后导航到这些内容并显示。 下图对此进行演示。
可以显示来自以下位置的宽松 XAML 文件:
本地机器、Intranet 或 Internet 上的网站。
Universal Naming Convention (UNC) 文件共享。
本地磁盘。
宽松 XAML 文件可添加到浏览器的收藏夹中,或作为浏览器的主页。
注意 |
---|
有关如何发布和启动宽松 XAML 页的更多信息,请参见部署 WPF 应用程序 (WPF)。 |
宽松 XAML 方面的一个限制是,只能承载以部分信任能安全运行的内容。 例如,Window 不能作为宽松 XAML 文件的根元素。 有关更多信息,请参见 WPF 部分信任安全。
使用框架导航到 HTML 文件
您可能已想到,还可以导航到 HTML。 您只需提供一个使用 http 方案的 URI 即可。 例如,下面的 XAML 演示一个导航到 HTML 页的 Frame。
<Frame Source="https://www.microsoft.com/default.aspx" />
导航到 HTML 需要特殊的权限。 例如,不能从在 Internet 区域部分信任安全沙箱中运行的 XBAP 进行导航。有关更多信息,请参见 WPF 部分信任安全。
使用 WebBrowser 控件导航到 HTML 文件
WebBrowser 控件支持 HTML 文档承载、导航和脚本/托管代码互操作性。 有关 WebBrowser 控件的详细信息,请参见 WebBrowser。
与 Frame 一样,使用 WebBrowser 导航至 HTML 需要特殊的权限。 例如,从不完全可信的应用程序,您只能导航到位于源站点上的 HTML。 有关更多信息,请参见 WPF 部分信任安全。
导航到自定义对象
如果有存储为自定义对象的数据,那么显示该数据的一种方法是创建一个 Page,并且其内容绑定到这些对象(请参见数据绑定概述)。 如果不希望只为显示这些对象而造成创建整页的开销,可以改为直接导航到这些对象。
考虑下面代码中实现的 Person 类。
Namespace SDKSample
Public Class Person
Private _name As String
Private _favoriteColor As Color
Public Sub New()
End Sub
Public Sub New(ByVal name As String, ByVal favoriteColor As Color)
Me._name = name
Me._favoriteColor = favoriteColor
End Sub
Public Property Name() As String
Get
Return Me._name
End Get
Set(ByVal value As String)
Me._name = value
End Set
End Property
Public Property FavoriteColor() As Color
Get
Return Me._favoriteColor
End Get
Set(ByVal value As Color)
Me._favoriteColor = value
End Set
End Property
End Class
End Namespace
using System.Windows.Media; // Color
namespace SDKSample
{
public class Person
{
string name;
Color favoriteColor;
public Person() { }
public Person(string name, Color favoriteColor)
{
this.name = name;
this.favoriteColor = favoriteColor;
}
public string Name
{
get { return this.name; }
set { this.name = value; }
}
public Color FavoriteColor
{
get { return this.favoriteColor; }
set { this.favoriteColor = value; }
}
}
}
为了导航到该对象,可以调用 NavigationWindow.Navigate 方法,如下面的代码所示。
<Page
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.HomePage"
WindowTitle="Page that Navigates to an Object">
...
<Hyperlink Name="hyperlink" Click="hyperlink_Click">
Navigate to Nancy Davolio
</Hyperlink>
...
</Page>
Namespace SDKSample
Partial Public Class HomePage
Inherits Page
Public Sub New()
InitializeComponent()
End Sub
Private Sub hyperlink_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
Dim person As New Person("Nancy Davolio", Colors.Yellow)
Me.NavigationService.Navigate(person)
End Sub
End Class
End Namespace
using System.Windows; // RoutedEventArgs
using System.Windows.Controls; // Page
using System.Windows.Media; // Colors
namespace SDKSample
{
public partial class HomePage : Page
{
public HomePage()
{
InitializeComponent();
}
void hyperlink_Click(object sender, RoutedEventArgs e)
{
Person person = new Person("Nancy Davolio", Colors.Yellow);
this.NavigationService.Navigate(person);
}
}
}
下图演示了结果。
从图中可以看到,显示的内容没有用处。 实际上,显示的值是 Person 对象的 ToString 方法的返回值;默认情况下,这是 WPF 唯一可用于表示对象的值。 重写 ToString 方法可以返回更有意义的信息,但该信息仍只是字符串值。 可使用的一种利用 WPF 演示功能的方法是使用数据模板。 可以实现一个数据模板,WPF 可将其与某种特定类型的对象相关联。 下面的代码演示 Person 对象的数据模板。
<Application
xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:SDKSample"
x:Class="SDKSample.App"
StartupUri="HomePage.xaml">
<Application.Resources>
<!-- Data Template for the Person Class -->
<DataTemplate DataType="{x:Type local:Person}">
<TextBlock xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation">
<TextBlock FontWeight="Bold">Name:</TextBlock>
<TextBlock Text="{Binding Path=Name}" />
<LineBreak />
<TextBlock FontWeight="Bold">Favorite Color:</TextBlock>
<TextBlock Text="{Binding Path=FavoriteColor}" />
</TextBlock>
</DataTemplate>
</Application.Resources>
</Application>
在这里,数据模板使用 DataType 特性中的 x:Type 标记扩展与 Person 类型关联。 该数据模板随后将 TextBlock 元素(请参见 TextBlock)绑定到 Person 类的属性。 下图演示 Person 对象更新后的外观。
此方法的一个优点是一致性,这是因为能重用数据模板,从而在应用程序的任何位置一致地显示对象。
有关数据模板的更多信息,请参见数据模板化概述。
安全性
通过 WPF 导航支持,可以在 Internet 上导航到 XBAPs,应用程序也可以承载第三方内容。 为了防止应用程序和用户受到有害行为的干扰,WPF 提供多种安全功能,这些功能将在 安全性 (WPF)和 WPF 部分信任安全中讨论。