结构化导航概述Structured Navigation Overview

可以由 XAML 浏览器应用程序(XBAP)、FrameNavigationWindow 承载的内容由可由包统一资源标识符(Uri)标识并通过超链接导航到的页面组成。Content that can be hosted by an XAML browser application (XBAP), a Frame, or a NavigationWindow is composed of pages that can be identified by pack uniform resource identifiers (URIs) and navigated to by hyperlinks. 页面的结构以及导航页面的方式(通过超链接来定义)称为导航拓扑。The structure of pages and the ways in which they can be navigated, as defined by hyperlinks, is known as a navigation topology. 此类拓扑适合各种应用程序类型,尤其适合在文档之间导航的应用程序类型。Such a topology suits a variety of application types, particularly those that navigate through documents. 对于此类应用程序,用户可以从一个页面导航到另一个页面,并且其中任一页面都无需了解另一页面的任何信息。For such applications, the user can navigate from one page to another page without either page needing to know anything about the other.

但是,对于其他类型的应用程序,在其页面之间导航时,确实需要了解这些页面信息。However, other types of applications have pages that do need to know when they have been navigated between. 例如,假设一个人力资源应用程序,它具有一个列出组织中所有员工的页面,即“员工列表”页。For example, consider a human resources application that has one page to list all the employees in an organization—the "List Employees" page. 此页还允许用户通过单击超链接添加新员工。This page could also allow users to add a new employee by clicking a hyperlink. 单击超链接后,页面会导航到“添加员工”页以收集新员工的详细信息,并将其返回到“员工列表”页以创建新员工并更新列表。When clicked, the page navigates to an "Add an Employee" page to gather the new employee's details and return them to the "List Employees" page to create the new employee and update the list. 这种样式的导航与调用方法来执行某些处理并返回值(称为结构化编程)类似。This style of navigation is similar to calling a method to perform some processing and return a value, which is known as structured programming. 同样,这种样式的导航称为结构化导航As such, this style of navigation is known as structured navigation.

Page 类不实现对结构化导航的支持。The Page class doesn't implement support for structured navigation. 相反,PageFunction<T> 类派生自 Page,并将其与结构化导航所需的基本构造进行扩展。Instead, the PageFunction<T> class derives from Page and extends it with the basic constructs required for structured navigation. 本主题演示如何使用 PageFunction<T>建立结构化导航。This topic shows how to establish structured navigation using PageFunction<T>.

结构化导航Structured Navigation

在结构化导航中,当一个页面调用另一个页面时需要以下部分或全部行为:When one page calls another page in a structured navigation, some or all of the following behaviors are required:

  • 调用页导航到被调用页,并且可以选择性地传递被调用页所需的参数。The calling page navigates to the called page, optionally passing parameters required by the called page.

  • 当用户已使用完调用页时,被调用页将专门返回到调用页,并且可以:The called page, when a user has completed using the calling page, returns specifically to the calling page, optionally:

    • 返回描述调用页是如何完成(例如,用户按的是“确定”按钮还是“取消”按钮)的状态信息。Returning state information that describes how the calling page was completed (for example, whether a user pressed an OK button or a Cancel button).

    • 返回从用户那里收集的数据(例如,新员工的详细信息)。Returning that data that was collected from the user (for example, new employee details).

  • 当调用页返回到被调用页时,被调用页会从导航历史记录中删除,以便将被调用页的一个实例与另一个实例隔离开。When the calling page returns to the called page, the called page is removed from navigation history to isolate one instance of a called page from another.

下图演示了这些行为:These behaviors are illustrated by the following figure:

屏幕截图显示调用页和被调用页之间的流。

可以通过使用 PageFunction<T> 作为被调用页来实现这些行为。You can implement these behaviors by using a PageFunction<T> as the called page.

使用 PageFunction 进行结构化导航Structured Navigation with PageFunction

本主题演示如何实现涉及单个 PageFunction<T>的结构化导航的基本机制。This topic shows how to implement the basic mechanics of structured navigation involving a single PageFunction<T>. 在此示例中,Page 调用 PageFunction<T> 以获取用户的 String 值并将其返回。In this sample, a Page calls a PageFunction<T> to get a String value from the user and return it.

创建调用页Creating a Calling Page

调用 PageFunction<T> 的页可以是 PagePageFunction<T>The page that calls a PageFunction<T> can be either a Page or a PageFunction<T>. 在此示例中,它是一个 Page,如下面的代码所示。In this example, it is a Page, as shown in the following code.

<Page 
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="StructuredNavigationSample.CallingPage"
    WindowTitle="Calling Page" 
    WindowWidth="250" WindowHeight="150">
</Page>
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;

namespace StructuredNavigationSample
{
    public partial class CallingPage : Page
    {
        public CallingPage()
        {
            InitializeComponent();
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Navigation

Namespace StructuredNavigationSample

Public Class CallingPage
    Inherits Page
    Public Sub New()
        Me.InitializeComponent()
}
End Sub
    }
}
End Class

End Namespace

创建要调用的页函数Creating a Page Function to Call

由于调用页可以使用被调用页来从用户那里收集和返回数据,因此 PageFunction<T> 是作为泛型类实现的,其类型参数指定被调用页将返回的值的类型。Because the calling page can use the called page to collect and return data from the user, PageFunction<T> is implemented as a generic class whose type argument specifies the type of the value that the called page will return. 下面的代码演示了被调用页的初始实现,该实现使用返回 StringPageFunction<T>The following code shows the initial implementation of the called page, using a PageFunction<T>, which returns a String.

<PageFunction
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:sys="clr-namespace:System;assembly=mscorlib" 
    x:Class="StructuredNavigationSample.CalledPageFunction"
    x:TypeArguments="sys:String"
    Title="Page Function" 
    WindowWidth="250" WindowHeight="150">

  <Grid Margin="10">

    <Grid.ColumnDefinitions>
      <ColumnDefinition Width="Auto" />
      <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <Grid.RowDefinitions>
      <RowDefinition Height="Auto" />
      <RowDefinition />
    </Grid.RowDefinitions>

    <!-- Data -->
    <Label Grid.Column="0" Grid.Row="0">DataItem1:</Label>
    <TextBox Grid.Column="1" Grid.Row="0" Name="dataItem1TextBox"></TextBox>

    <!-- Accept/Cancel buttons -->
    <TextBlock Grid.Column="1" Grid.Row="1" HorizontalAlignment="Right">
      <Button Name="okButton" IsDefault="True" MinWidth="50">OK</Button>
      <Button Name="cancelButton" IsCancel="True" MinWidth="50">Cancel</Button>
    </TextBlock>

  </Grid>

</PageFunction>
using System;
using System.Windows;
using System.Windows.Navigation;

namespace StructuredNavigationSample
{
    public partial class CalledPageFunction : PageFunction<String>
    {
        public CalledPageFunction()
        {
            InitializeComponent();
        }
Imports System.Windows
Imports System.Windows.Navigation

Namespace StructuredNavigationSample

Public Class CalledPageFunction
    Inherits PageFunction(Of String)
    Public Sub New()
        Me.InitializeComponent()
    End Sub
    }
}
End Class

End Namespace

PageFunction<T> 的声明类似于添加了类型参数的 Page 的声明。The declaration of a PageFunction<T> is similar to the declaration of a Page with the addition of the type arguments. 从代码示语例中可以看出,在 XAMLXAML 标记和代码隐藏中均指定了类型自变量,前者使用 x:TypeArguments 属性,后者使用标准的泛型类型参数语法。As you can see from the code example, the type arguments are specified in both XAMLXAML markup, using the x:TypeArguments attribute, and code-behind, using standard generic type argument syntax.

不必仅使用 .NET Framework 类作为类型实参。You don't have to use only .NET Framework classes as type arguments. 可以调用 PageFunction<T> 来收集抽象为自定义类型的域特定数据。A PageFunction<T> could be called to gather domain-specific data that is abstracted as a custom type. 下面的代码演示如何使用自定义类型作为 PageFunction<T>的类型参数。The following code shows how to use a custom type as a type argument for a PageFunction<T>.

namespace SDKSample
{
    public class CustomType
    {
Public Class CustomType
    }
}
End Class
<PageFunction
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:SDKSample" 
    x:Class="SDKSample.CustomTypePageFunction"
    x:TypeArguments="local:CustomType">
</PageFunction>
using System.Windows.Navigation;

namespace SDKSample
{
    public partial class CustomTypePageFunction : PageFunction<CustomType>
    {
Partial Public Class CustomTypePageFunction
    Inherits System.Windows.Navigation.PageFunction(Of CustomType)
    }
}
End Class

PageFunction<T> 的类型自变量为调用页和被调用页之间的通信提供基础,以下部分对此进行了讨论。The type arguments for the PageFunction<T> provide the foundation for the communication between a calling page and the called page, which are discussed in the following sections.

正如您将看到的,用 PageFunction<T> 的声明标识的类型在将数据从 PageFunction<T> 返回到调用页时起着重要的作用。As you'll see, the type that is identified with the declaration of a PageFunction<T> plays an important role in returning data from a PageFunction<T> to the calling page.

调用 PageFunction 和传递参数Calling a PageFunction and Passing Parameters

若要调用页,调用页必须实例化被调用的页,并使用 Navigate 方法导航到它。To call a page, the calling page must instantiate the called page and navigate to it using the Navigate method. 这使得调用页可以将初始数据传递给被调用页,例如,被调用页收集的数据的默认值。This allows the calling page to pass initial data to the called page, such as default values for the data being gathered by the called page.

下面的代码显示了使用非参数构造函数的被调用页,以接受来自调用页的参数。The following code shows the called page with a non-parameterless constructor to accept parameters from the calling page.

using System;
using System.Windows;
using System.Windows.Navigation;

namespace StructuredNavigationSample
{
    public partial class CalledPageFunction : PageFunction<String>
    {
Imports System.Windows
Imports System.Windows.Navigation

Namespace StructuredNavigationSample

Public Class CalledPageFunction
    Inherits PageFunction(Of String)
public CalledPageFunction(string initialDataItem1Value)
{
    InitializeComponent();

Public Sub New(ByVal initialDataItem1Value As String)
    Me.InitializeComponent()
    // Set initial value
    this.dataItem1TextBox.Text = initialDataItem1Value;
}
    ' Set initial value
    Me.dataItem1TextBox.Text = initialDataItem1Value
End Sub
    }
}
End Class

End Namespace

下面的代码演示了用于处理 HyperlinkClick 事件的调用页,以实例化被调用的页并向其传递初始字符串值。The following code shows the calling page handling the Click event of the Hyperlink to instantiate the called page and pass it an initial string value.

<Hyperlink Name="pageFunctionHyperlink">Call Page Function</Hyperlink>
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;

namespace StructuredNavigationSample
{
    public partial class CallingPage : Page
    {
        public CallingPage()
        {
            InitializeComponent();
            this.pageFunctionHyperlink.Click += new RoutedEventHandler(pageFunctionHyperlink_Click);
        }
        void pageFunctionHyperlink_Click(object sender, RoutedEventArgs e)
        {

            // Instantiate and navigate to page function
            CalledPageFunction CalledPageFunction = new CalledPageFunction("Initial Data Item Value");
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Navigation

Namespace StructuredNavigationSample

Public Class CallingPage
    Inherits Page
    Public Sub New()
        Me.InitializeComponent()
        AddHandler Me.pageFunctionHyperlink.Click, New RoutedEventHandler(AddressOf Me.pageFunctionHyperlink_Click)
    End Sub
    Private Sub pageFunctionHyperlink_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
}
End Sub
    }
}
End Class

End Namespace

不必向被调用页传递参数。You are not required to pass parameters to the called page. 可以执行以下操作:Instead, you could do the following:

但是,你不久就会看到,你仍然需要使用代码来实例化并导航到被调用页,以收集被调用页返回的数据。But, as you'll see shortly, you'll still need use code to instantiate and navigate to the called page to collect the data returned by the called page. 出于此原因,PageFunction<T> 需要保持活动状态;否则,下一次导航到 PageFunction<T>时,WPF 将使用无参数构造函数实例化 PageFunction<T>For this reason, the PageFunction<T> needs to be kept alive; otherwise, the next time you navigate to the PageFunction<T>, WPF instantiates the PageFunction<T> using the parameterless constructor.

但是,在被调用页返回前,需要返回可以由调用页检索的数据。Before the called page can return, however, it needs to return data that can be retrieved by the calling page.

将任务的任务结果和任务数据返回到调用页Returning Task Result and Task Data from a Task to a Calling Page

在用户使用完被调用页后(在本示例中通过按“确定”或“取消”按钮来表示),被调用页需要返回。Once the user has finished using the called page, signified in this example by pressing either the OK or Cancel buttons, the called page needs to return. 因为调用页已使用被调用页来从用户那里收集数据,所以调用页需要两种类型的信息:Since the calling page used the called page to collect data from the user, the calling page requires two types of information:

  1. 用户是否取消了被调用页(在此示例中通过按“确定”或“取消”按钮)。Whether the user canceled the called page (by pressing either the OK button or the Cancel button in this example). 这使得调用页可以确定是否处理调用页从用户那里收集的数据。This allows the calling page to determine whether to process the data that the calling page gathered from the user.

  2. 用户提供的数据。The data that was provided by the user.

若要返回信息,PageFunction<T> 实现 OnReturn 方法。To return information, PageFunction<T> implements the OnReturn method. 下面的代码演示如何调用它。The following code shows how to call it.

using System;
using System.Windows;
using System.Windows.Navigation;

namespace StructuredNavigationSample
{
    public partial class CalledPageFunction : PageFunction<String>
    {
Imports System.Windows
Imports System.Windows.Navigation

Namespace StructuredNavigationSample

Public Class CalledPageFunction
    Inherits PageFunction(Of String)
        void okButton_Click(object sender, RoutedEventArgs e)
        {
            // Accept when Ok button is clicked
            OnReturn(new ReturnEventArgs<string>(this.dataItem1TextBox.Text));
        }

        void cancelButton_Click(object sender, RoutedEventArgs e)
        {
            // Cancel
            OnReturn(null);
        }
    }
}
    Private Sub okButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
        ' Accept when Ok button is clicked
        Me.OnReturn(New ReturnEventArgs(Of String)(Me.dataItem1TextBox.Text))
    End Sub

    Private Sub cancelButton_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
        ' Cancel
        Me.OnReturn(Nothing)
    End Sub
End Class

End Namespace

在此示例中,如果用户按“取消”按钮,则会向调用页返回 null 值。In this example, if a user presses the Cancel button, a value of null is returned to the calling page. 如果按“确定”按钮,则返回用户提供的字符串值。If the OK button is pressed instead, the string value provided by the user is returned. OnReturn 是一种 protected virtual 方法,通过调用该方法可以将数据返回到调用页。OnReturn is a protected virtual method that you call to return your data to the calling page. 需要将数据打包到泛型 ReturnEventArgs<T> 类型的实例中,其 type 参数指定 Result 返回的值的类型。Your data needs to be packaged in an instance of the generic ReturnEventArgs<T> type, whose type argument specifies the type of value that Result returns. 这样,当你使用特定类型参数声明 PageFunction<T> 时,你将声明 PageFunction<T> 将返回类型参数指定的类型的实例。In this way, when you declare a PageFunction<T> with a particular type argument, you are stating that a PageFunction<T> will return an instance of the type that is specified by the type argument. 在此示例中,类型参数和,因此,返回值的类型为 StringIn this example, the type argument and, consequently, the return value is of type String.

调用 OnReturn 时,调用页需要某种方式来接收 PageFunction<T>的返回值。When OnReturn is called, the calling page needs some way of receiving the return value of the PageFunction<T>. 出于此原因,PageFunction<T> 实现了用于调用要处理的页的 Return 事件。For this reason, PageFunction<T> implements the Return event for calling pages to handle. 调用 OnReturn 时,将引发 Return,因此调用页可以向 Return 注册以接收通知。When OnReturn is called, Return is raised, so the calling page can register with Return to receive the notification.

using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;

namespace StructuredNavigationSample
{
    public partial class CallingPage : Page
    {
Imports System.Windows
Imports System.Windows.Controls
Imports System.Windows.Navigation

Namespace StructuredNavigationSample

Public Class CallingPage
    Inherits Page
        void pageFunctionHyperlink_Click(object sender, RoutedEventArgs e)
        {

            // Instantiate and navigate to page function
            CalledPageFunction CalledPageFunction = new CalledPageFunction("Initial Data Item Value");
            CalledPageFunction.Return += pageFunction_Return;
            this.NavigationService.Navigate(CalledPageFunction);
        }
        void pageFunction_Return(object sender, ReturnEventArgs<string> e)
        {
            this.pageFunctionResultsTextBlock.Visibility = Visibility.Visible;

            // Display result
            this.pageFunctionResultsTextBlock.Text = (e != null ? "Accepted" : "Canceled");

            // If page function returned, display result and data
            if (e != null)
            {
                this.pageFunctionResultsTextBlock.Text += "\n" + e.Result;
            }
        }
    }
}
    Private Sub pageFunctionHyperlink_Click(ByVal sender As Object, ByVal e As RoutedEventArgs)
        ' Instantiate and navigate to page function
        Dim calledPageFunction As New CalledPageFunction("Initial Data Item Value")
        AddHandler calledPageFunction.Return, New ReturnEventHandler(Of String)(AddressOf Me.calledPageFunction_Return)
        MyBase.NavigationService.Navigate(calledPageFunction)
    End Sub
    Private Sub calledPageFunction_Return(ByVal sender As Object, ByVal e As ReturnEventArgs(Of String))

        Me.pageFunctionResultsTextBlock.Visibility = Windows.Visibility.Visible

        ' Display result
        Me.pageFunctionResultsTextBlock.Text = IIf((Not e Is Nothing), "Accepted", "Canceled")

        ' If page function returned, display result and data
        If (Not e Is Nothing) Then
            Me.pageFunctionResultsTextBlock.Text = (Me.pageFunctionResultsTextBlock.Text & ChrW(10) & e.Result)
        End If

    End Sub
End Class

End Namespace

当任务完成时删除任务页Removing Task Pages When a Task Completes

当被调用页返回,并且用户未取消被调用页时,调用页将处理由用户提供并且从被调用页返回的数据。When a called page returns, and the user didn't cancel the called page, the calling page will process the data that was provided by the user and also returned from the called page. 这种方式的数据采集通常是独立的活动;当被调用页返回时,调用页需要创建并导航到新的调用页来捕获更多数据。Data acquisition in this way is usually an isolated activity; when the called page returns, the calling page needs to create and navigate to a new calling page to capture more data.

不过,除非已从日志中删除被调用页,否则用户将能够向后导航到调用页的上一个实例。However, unless a called page is removed from the journal, a user will be able to navigate back to a previous instance of the calling page. 是否在日记中保留 PageFunction<T>RemoveFromJournal 属性确定。Whether a PageFunction<T> is retained in the journal is determined by the RemoveFromJournal property. 默认情况下,当调用 OnReturn 时,将自动删除页面函数,因为 RemoveFromJournal 设置为 trueBy default, a page function is automatically removed when OnReturn is called because RemoveFromJournal is set to true. 若要在调用 OnReturn 后在导航历史记录中保留页面功能,请将 RemoveFromJournal 设置为 "false"。To keep a page function in navigation history after OnReturn is called, set RemoveFromJournal to false.

其他类型的结构化导航Other Types of Structured Navigation

本主题演示了支持调用/返回结构化导航的 PageFunction<T> 的最基本用法。This topic illustrates the most basic use of a PageFunction<T> to support call/return structured navigation. 这一基础使你能够创建更复杂的结构化导航类型。This foundation provides you with the ability to create more complex types of structured navigation.

例如,有时调用页需要多个页面来从用户那里收集足够的数据或者执行任务。For example, sometimes multiple pages are required by a calling page to gather enough data from a user or to perform a task. 多个页面的使用称为“向导”。The use of multiple pages is referred to as a "wizard".

在其他情况下,应用程序可能具有依赖于结构化导航来有效操作的复杂导航拓扑。In other cases, applications may have complex navigation topologies that depend on structured navigation to operate effectively. 有关详细信息,请参阅导航拓扑概述For more information, see Navigation Topologies Overview.

另请参阅See also