本主题将讨论基本的导航概念,并演示如何创建一个在两个页面之间进行导航的应用。
有关为你的应用选择最佳导航模式的帮助,请参阅导航模式。
在操作时请参阅平面导航和分层导航模式,它们是应用功能大全系列的一部分。
路线图: 本主题与其他主题有何关联?请参阅:
你可以为你的应用创建多个页面,并支持用户在应用中的页面之间进行导航,类似于在一个网站的网页之间进行导航。Microsoft Visual Studio 包含页面模板,这些模板可为使用 C# 或 Microsoft Visual Basic 的 Windows 运行时应用提供基本的导航支持。 在本主题中,我们使用页面模板创建一个支持导航的简单应用。
注意
当我们讨论 Windows 运行时应用的导航时,我们是指在一个应用的页面之间进行导航,而不是在多个应用之间进行导航。
先决条件
本主题假定你可以使用 C# 或 Visual Basic 创建基本的 Windows 运行时应用。有关创建你的第一个 Windows 运行时应用的说明,请参阅使用 C# 或 Visual Basic 创建你的第一个 Windows 运行时应用。
创建导航应用
创建空白应用
- 在 Visual Studio 菜单上,选择“文件”>“新建项目”。
- 在“新建项目”对话框的左侧窗格中,选择“Visual C#”、“Visual Basic”或“Visual C++”节点。
- 在中心窗格中,选择“空白应用程序”。
- 在“名称”框中,输入 BlankApp,然后选择“确定”按钮。
此刻,解决方案已创建并且项目文件显示在“解决方案资源管理器”中。有关项目文件的详细信息,请参阅适用于 Windows 运行时应用的 C#、VB 以及 C++ 项目模板。
要点 首次运行 Visual Studio 时,它会提示你获取开发人员许可证。有关详细信息,请参阅获取开发人员许可证。
- 若要运行程序,请在 Visual Studio 菜单上,选择“调试”>“启动调试”或按 F5。
随即将显示一个空白页面。
- 按 Shift + F5 可停止调试并返回到 Visual Studio。
接下来,在项目中添加两个页面。 我们将在这两个页面之间进行导航。 执行以下步骤两次以添加两个页面。
添加基本页面
- 在“解决方案资源管理器”中,打开 BlankApp 项目节点的快捷菜单,然后选择“添加”>“新建项目”。
- 在“添加新项”对话框中,选择中间窗格的“空白页面”。
- 在“名称”框中,输入 page1(或 page2)并选择“添加”按钮。
当你执行完上述步骤两次之后,如下文件应被添加到项目中。
- BasicPage1.xaml
- BasicPage1.xaml.cs 或 BasicPage1.xaml.vb
- BasicPage2.xaml
- BasicPage2.xaml.cs 或 BasicPage2.xaml.vb
现在,我们需要使用刚才在应用中添加的页面。 对 BasicPage1.xaml 执行如下更改。
找到名为
pageTitle
的 TextBlock 元素,并将 Text 属性更改为Page 1
。XAML 应如下所示("..." 代表未更改的其他属性):<TextBlock x:Name="pageTitle" Text="Page 1" .../>
将下列 XAML 作为第二个子元素添加到根 Grid。 StackPanel 元素应当为包含“后退”按钮和页面标题的 Grid的同级。
<StackPanel Grid.Row="1" Margin="120,0,120,60"> <HyperlinkButton Content="Click to go to page 2" Click="HyperlinkButton_Click"/></StackPanel>
对 BasicPage2.xaml 执行如下更改。
找到名为
pageTitle
的 TextBlock 元素,并将 Text 属性更改为Page 2
。XAML 应如下所示:<TextBlock x:Name="pageTitle" Grid.Column="1" Text="Page 2" Style="{StaticResource PageHeaderTextStyle}"/>
将下列 XAML 作为第二个子元素添加到根 Grid。 StackPanel 元素应当为包含“后退”按钮和页面标题的 Grid的同级。
<StackPanel Grid.Row="1" Margin="120,0,120,60"> <TextBlock HorizontalAlignment="Left" Name="tb1" Text="Hello World!"/></StackPanel>
将如下代码添加到 BasicPage1.xaml.cs 或 BasicPage1.xaml.vb 中的 BasicPage1
类。
private void HyperlinkButton_Click(object sender, RoutedEventArgs e){ this.Frame.Navigate(typeof(BasicPage2));}
现在,新页面已经准备好,我们需要使 BasicPage1
成为在应用启动时最先显示的页面。 打开 app.xaml.cs/vb 并将 OnLaunched
方法更改为使用 BasicPage1
而不是 BlankPage
来调用 Frame.Navigate。 相关代码行如下:
if(!rootFrame.Navigate(typeof(BasicPage1), e.Arguments)) { ... }
注意 如果导航至应用的初始窗口框架失败,则此处的代码会使用 Navigate 的返回值引发应用异常。当Navigate 返回 true,导航即会发生。
现在你可以测试该应用。 启动应用,并单击显示文字“单击以转到第 2 页”的链接。 将显示第二页,且该页面顶部应显示“第 2 页”。 在 Windows 应用商店应用中,请注意该页面标题的左侧有一个“后退”按钮。 单击该按钮可返回第一页。在 Windows Phone 应用商店应用中,单击手机的“后退”按钮以返回第一页。
Frame 和 Page 类
在将更多功能添加到应用中之前,让我们来看看我们所添加的页面如何为应用提供导航支持。
文件 App.xaml.cs/vb/cpp 会创建 Frame(如果之前不存在),且会将 Frame 作为当前窗口的内容。 如果框架内容为空,则应用会导航到代码隐藏 App.xaml 中指定的主页。例如,在网格应用中,代码为rootFrame.Navigate(typeof(GroupedItemsPage), "AllGroups") )
。
Frame 类主要负责导航和实现方法,例如 Navigate、GoBack 和 GoForward。 使用 Navigate 方法在 Frame中显示内容。 在前一个示例中,App.OnLaunched
方法将会创建一个 Frame,并将 BasicPage1
传递给Navigate 方法。 然后,该方法将应用的当前窗口的内容设置为 Frame。其结果是,应用的窗口包含一个包含BasicPage1
的 Frame。
BasicPage1
是 Page 类的子类。 Page 类具有 Frame 属性,它是一种用于获得包含 Page 的 Frame 的只读属性。 当 HyperlinkButton 的 Click 事件处理程序调用 Frame.Navigate(typeof(BasicPage2))
时,应用窗口中的Frame 将会显示 BasicPage2
的内容。
挂起管理器
要点
以下项目模板中提供了 SuspensionManager 帮助程序类:
Windows 应用 | 中心应用、网格应用、拆分应用 |
---|---|
Windows Phone 应用 | 中心应用、透视应用 |
通用应用 | 中心应用 |
SuspensionManager 帮助程序类不与空白应用模板一起提供。
在设置过程中,SuspensionManager
会注册 Frame。 SuspensionManager
是模板中 Common 文件夹中提供的帮助程序类,并且它提供的实现可用于在应用终止时存储和加载状态。
所有应用都会按照操作系统的指示在应用程序生命周期中移动。 不论何时应用由于某些原因(如资源限制、关闭、重启等等)被系统终止,你作为开发人员必须在应用继续时立即还原数据。 提供 SuspensionManager
有助于你执行此任务。
SuspensionManager
会捕获全局会话状态以简化应用程序的进程生命周期管理。 会话状态会在多种情形下自动清除,且应仅用于存储便于在会话之间携带的信息,而在应用程序崩溃或进行升级时应丢弃该信息。 此信息基本上会包含过渡 UI 数据。
SuspensionManager
存在两种属性:SessionState 和 KnownTypes。
- SessionState 提供当前会话的全局会话状态的访问权限。 此状态由 SaveAsync 方法序列化且由 RestoreAsync 方法还原。 所有数据使用 DataContractSerialization 保存和还原且应尽可能地压缩。 强烈建议字符串为其他自包含数据类型。
- KnownTypes 存储了为 DataContractSerializer 提供的自定义类型列表,在处于读写会话状态时,SaveAsync 和 RestoreAsync 方法会使用该 DataContractSerializer。 可能会添加最初为空的其他类型以自定义序列化过程。
SuspensionManager
在 SessionState 字典中存储状态。 字典会针对唯一绑定到 Frame 的密钥存储 FrameState。 每个 FrameState 字典在该特定框架的导航状态下保留每页的状态。 每个页面会存储导航参数以及用户决定添加的任何其他状态。
以下为其工作原理:当创建 Frame 后,如果你希望为该框架存储状态,则应立即注册它。 使用以下调用(SuspensionManager.RegisterFrame(rootFrame, "AppFrame"))
进行注册。 每个框架都应有与其关联的唯一密钥。 通常大部分应用仅有一个框架。 如果你声明其他框架,则还需要注册该框架。 当注册某个框架后,会在该框架上设置两个附加属性。 第一个属性是与框架关联的密钥,而第二个属性是与框架关联的会话状态字典。 以前注册的框架会立即还原其导航和状态。 也可以取消注册框架,则所有导航状态和历史记录都会被丢弃。
现在我们来看看重要的调用:SaveAsync
和 RestoreAsync
。 SaveAsync
用于保存整个 SessionState。 已通过 SuspensionManager.RegisterFrame 注册的所有框架还将保留其当前导航堆栈,该堆栈依次为其活动页面提供保存其数据的机会。 然后,根据 ApplicationData 的定义,使用 DataContractSerializer 对 SessionState 进行序列化并将其写入本地文件夹中存储的文件。
RestoreAsync 用于读取以前保存的 SessionState。 已使用 RegisterFrame 注册的所有框架还将还原其以前的导航状态,该状态为其活动页面提供还原其状态的机会。 当再次在 SaveAsync
中时,DataContractSerializer 用于取消序列化应用程序的本地文件夹的文件中存储的状态。
当用户尝试存储其应用状态时,通常会遇到两种常见错误。
- 单个页面存储的类型必须可以使用 C# 和 VB 由 DataContractSerializer 序列化。 为此,必须注册任何自定义类型,然后才能保存或还原该类型。
SuspensionManager
提供了 KnownTypes 集合,该集合将集合中的类型传递到 DataContractSerializer。 当调用SuspensionManager
以还原 App.xaml 的代码隐藏的 OnLaunched 覆盖中的状态时,最好在应用构造函数中注册类型。public App() { this.InitializeComponent(); this.Suspending += OnSuspending; SuspensionManager.KnownTypes.Add(typeof(MyCustomType)); }
- 使用导航传递的参数必须可以由平台序列化。 当我们保存和还原导航堆栈时,会调用
Frame.GetNavigationState()
和Frame.SetNavigationState()
。 这两个调用都使用内部序列化格式且在Frame.Navigate()
中作为参数传递的所有类型必须可以由平台序列化。
SuspensionManager
的使用在 NavigationHelper 的实施中被隐藏。
NavigationHelper 是页面的实现方式,该页面提供了以下重要便利:
除了提供所述的实现之外,还需要从在每个页面上实现的 OnNavigatedTo() 和 OnNavigatedFrom() 事件处理程序中调用 NavigationHelper。 当发生这些事件时,NavigationHelper 将调用特定于页面的 LoadState() 和 SaveState() 实现。 你可以在每个页面上自定义这些函数的实现。应使用它们分别替换 OnNavigatedTo() 和OnNavigatedFrom()。
注意 在 Windows Phone 上,当应用暂停时调用 OnNavigatedFrom()。当应用恢复时,不调用 OnNavigatedTo()。
当页面即将在 Frame 中显示时会调用 OnNavigatedFrom()。 当我们导航至新页面时,会加载与该页面关联的状态。 如果正在还原页面,则会还原之前为该页面保存的状态。 然后会调用 LoadState,这样每个页面才会响应。 LoadState 有两个参数,最初的导航参数传递至 OnNavigatedTo 和之前页面状态(如果存在)。
当页面不再显示在 Frame 时会调用 OnNavigatedFrom()。 当我们导航离开页面时,允许页面保存其当前状态。 将空字典传递至 SaveState()。 每个页面都可以覆盖 SaveState 并存储键控字典中的对象(字符串至对象)。 然后,此字典与页面关联并添加到 SessionState,SuspensionManager
为给定的框架跟踪该 SessionState。
注意
- 单个页面中存储的任何数据都必须可供 DataContractSerializer 进行序列化。
- 仅存储此处的过渡 UI 信息也很重要,因为如果应用由于终止之外的任何原因被关闭,则此状态将丢失。
页面模板中的导航支持
在创建导航页面时,我们使用了“基本页”模板。 该模板和支持导航的其他模板,可创建一个页面,且在页面的左上角提供一个“后退”按钮。 该按钮的样式已设置为仅在它被启用时才会显示。 这就是你在第一页上看不到“后退”按钮,而在第二页上看见它的原因。
以下页面模板提供相同的导航支持。
- 基本页
- 组详细信息页
- 分组项页
- 项详细信息页
- 项页
- 拆分页
- 中心页
- 搜索结果页面
在页面之间传递信息
我们的应用可在两个页面之间进行导航,但它真的没有做什么有趣的事。 当一个应用包含多个页面时,这些页面经常需要共享信息。 让我们将一些信息从第一页传递到第二页。
在 BasicPage1.xaml 中,用 XAML 替换你之前添加的 StackPanel。
<StackPanel Grid.Row="1" Margin="120,0,120,60"> <TextBlock Text="Enter your name"/> <TextBox Width="200" HorizontalAlignment="Left" Name="tb1"/> <HyperlinkButton Content="Click to go to page 2" Click="HyperlinkButton_Click"/></StackPanel>
在 BasicPage1.xaml.cs 或 BasicPage1.xaml.vb 中,使用以下代码替换 HyperlinkButton_Click
事件处理程序。
private void HyperlinkButton_Click(object sender, RoutedEventArgs e){ this.Frame.Navigate(typeof(BasicPage2), tb1.Text);}
在 BasicPage2.xaml.cs 或 BasicPage2.xaml.vb 中,使用以下代码填写空的 navigationHelper_LoadState 方法:
private void navigationHelper_LoadState(object sender, LoadStateEventArgs e){ string name = e.NavigationParameter as string; if (!string.IsNullOrWhiteSpace(name)) { tb1.Text = "Hello, " + name; } else { tb1.Text = "Name is required. Go back and enter a name."; }}
运行应用,在文本框中输入你的名字,然后单击显示文字“单击以转到第 2 页”的链接。 当你在 HyperlinkButton的 Click 事件中调用 this.Frame.Navigate(typeof(BasicPage2), tb1.Text);
时,tb1.Text
属性将在 BasicPage2
加载时被传递。 然后,BlankPage2
的 navigationHelper_LoadState
方法从事件数据获得值,并用它显示一条消息。
缓存页面
当你运行上一个示例时,你可能已经注意到,如果你在 BasicPage2
上单击“后退”按钮,当 BasicPage1
上的TextBox 出现时,它是空的。 让我们假设,应用的用户想要返回到上一页进行更改。 如果 BasicPage1
有许多字段要填写,当应用返回到该页时,用户一定不会愿意看见重置的字段。 你可以使用 NavigationCacheMode 属性来指定对一个页面进行缓存。 在 BasicPage1
的构造函数中,将 NavigationCacheMode 设置为 Enabled。
public BasicPage1(){ this.InitializeComponent(); ... this.NavigationCacheMode = Windows.UI.Xaml.Navigation.NavigationCacheMode.Enabled;}
现在,当你运行应用并从 BasicPage2
导航回 BasicPage1
时,BasicPage1
上的 TextBox 将保留其值。
摘要
在本主题中,你学习了如何创建一个在页面之间进行导航的简单应用。 你学习了如何将信息从一个页面传递到另一个页面,以及如何指定对一个页面的状态进行缓存。
后续步骤
对于使用许多 Page 和 Frame 功能的完整示例,请参阅 XAML 导航示例。该示例包含此处未讨论的功能,包括:
- 检查导航堆栈(BackStack、ForwardStack)
- 检查 CanGoBack 或 CanGoForward 并将它们作为隐藏和显示导航 UI 的触发器来使用
- 有关缓存页面、GetNavigationState 和 CacheSize 的详细信息
- 取消缓存和 Navigating 事件