结构化导航概述

可以由 XAML browser application (XBAP)、FrameNavigationWindow 承载的内容由多个页面组成,这些页面可以通过 pack uniform resource identifiers (URIs) 来标识,并且可以通过超链接来导航。 页面的结构以及导航页面的方式(通过超链接来定义)称为导航拓扑。 这样的拓扑适合于各种应用程序类型,尤其适合于在文档之间导航的应用程序类型。 对于此类应用程序,用户可以从一个页面导航到另一个页面,并且任何页面都不需要知道对方的任何信息。

但是,对于其他类型的应用程序,当在其页面之间导航时,您确实需要知道这些页面信息。 以一个人力资源应用程序为例,它具有一个列出组织中的所有员工的页面,即“List Employees”(列出员工)页。 该页还允许用户通过单击超链接来添加新员工。 当单击此超链接时,页面将导航到“Add an Employee”(添加员工)页,以收集新员工的详细信息,并将其返回到“List Employees”(列出员工)页,以创建新员工并更新列表。 这种样式的导航与调用方法来执行某些处理并返回值(称为结构化编程)类似。 同样,这种样式的导航称为“结构化导航”。

Page 类未实现对结构化导航的支持。 PageFunction<T> 类派生自 Page,并使用结构化导航所需的基本构造来对它进行扩展。 本主题演示如何使用 PageFunction<T> 建立结构化导航。

本主题包括下列各节。

  • 结构化导航
  • 使用 PageFunction 进行结构化导航
  • 其他类型的结构化导航
  • 相关主题

结构化导航

当一个页面调用结构化导航中的另一页面时,需要下面的部分或全部行为:

  • 调用页导航到被调用页,并且可以选择传递被调用页所需的参数。

  • 当用户已使用完调用页时,被调用页将明确返回到调用页,并且可以:

    • 返回描述调用页是如何完成(例如,用户按的是 OK(确定)按钮还是 Cancel(取消)按钮)的状态信息。

    • 返回从用户那里收集的数据(例如,新员工的详细信息)。

  • 当调用页返回到被调用页时,被调用页会从导航历史记录中移除,以便将被调用页的一个实例与另一个实例分开。

下图说明了这些行为。

调用页与被调用页之间的流

您可以通过将 PageFunction<T> 用作被调用页来实现这些行为。

使用 PageFunction 进行结构化导航

本主题演示如何实现涉及一个 PageFunction<T> 的结构化导航的基本机制。 在本示例中,Page 调用 PageFunction<T>,以便从用户那里获取 String 值并返回该值。

创建调用页

调用 PageFunction<T> 的页可以是 Page,也可以是 PageFunction<T>。 在本示例中,它是 Page,如下面的代码所示。

<Page 
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://schemas.microsoft.com/winfx/2006/xaml"
    x:Class="StructuredNavigationSample.CallingPage"
    WindowTitle="Calling Page" 
    WindowWidth="250" WindowHeight="150">


...


</Page>
Imports System.Windows ' RoutedEventArgs, RoutedEventHandler, Visibility
Imports System.Windows.Controls ' Page
Imports System.Windows.Navigation ' ReturnEventArgs

Namespace StructuredNavigationSample

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


...


End Sub


...


End Class

End Namespace
using System.Windows; // RoutedEventArgs, RoutedEventHandler, Visibility
using System.Windows.Controls; // Page
using System.Windows.Navigation; // ReturnEventArgs

namespace StructuredNavigationSample
{
    public partial class CallingPage : Page
    {
        public CallingPage()
        {
            InitializeComponent();


...


}


...


    }
}

创建要调用的页面函数

因为调用页可以使用被调用页来从用户那里收集数据并返回数据,所以 PageFunction<T> 实现为一个泛型类,它的类型参数指定被调用页将返回的值的类型。 下面的代码示例显示被调用页的初始实现,它使用 PageFunction<T>,后者返回 String

<PageFunction
    xmlns="https://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="https://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>
Imports System ' String
Imports System.Windows ' RoutedEventArgs, RoutedEventHandler
Imports System.Windows.Navigation ' PageFunction

Namespace StructuredNavigationSample

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


...


End Class

End Namespace
using System; // String
using System.Windows; // RoutedEventArgs, RoutedEventHandler
using System.Windows.Navigation; // PageFunction

namespace StructuredNavigationSample
{
    public partial class CalledPageFunction : PageFunction<String>
    {
        public CalledPageFunction()
        {
            InitializeComponent();
        }


...


    }
}

PageFunction<T> 的声明与 Page 的声明类似,但增加了类型参数。 从代码示例中可以看出,在 XAML 标记和代码隐藏中均指定了类型参数,前者使用 x:TypeArguments 特性,后者使用标准的泛型参数语法。

不必仅使用 .NET Framework 类作为类型参数。 可以调用 PageFunction<T> 来收集特定于域的数据,这些数据抽象化为自定义类型。 下面的代码演示如何将自定义类型用作 PageFunction<T> 的类型参数。

Public Class CustomType


...


End Class
namespace SDKSample
{
    public class CustomType
    {


...


    }
}
<PageFunction
    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.CustomTypePageFunction"
    x:TypeArguments="local:CustomType">


...


</PageFunction>
Partial Public Class CustomTypePageFunction
    Inherits System.Windows.Navigation.PageFunction(Of CustomType)
using System.Windows.Navigation; // PageFunction

namespace SDKSample
{
    public partial class CustomTypePageFunction : PageFunction<CustomType>
    {

PageFunction<T> 的类型参数提供了调用页与被调用页之间的通信的基础,此通信将在后面几节介绍。

您将看到,使用 PageFunction<T> 的声明标识的类型在从 PageFunction<T> 向调用页返回数据方面扮演了重要的角色。

调用 PageFunction 和传递参数

若要调用页面,调用页必须实例化被调用页,并使用 Navigate 方法导航到它。 这使得调用页可以将初始数据传递给被调用页,例如,被调用页收集的数据的默认值。

下面的代码显示使用非默认构造函数从调用页接受参数的被调用页。

Imports System ' String
Imports System.Windows ' RoutedEventArgs, RoutedEventHandler
Imports System.Windows.Navigation ' PageFunction

Namespace StructuredNavigationSample

Public Class CalledPageFunction
    Inherits PageFunction(Of String)


...


Public Sub New(ByVal initialDataItem1Value As String)
    Me.InitializeComponent()


...


    ' Set initial value
    Me.dataItem1TextBox.Text = initialDataItem1Value
End Sub


...


End Class

End Namespace
using System; // String
using System.Windows; // RoutedEventArgs, RoutedEventHandler
using System.Windows.Navigation; // PageFunction

namespace StructuredNavigationSample
{
    public partial class CalledPageFunction : PageFunction<String>
    {


...


        public CalledPageFunction(string initialDataItem1Value)
        {
            InitializeComponent();



...


    // Set initial value
    this.dataItem1TextBox.Text = initialDataItem1Value;
}


...


    }
}

下面的代码显示调用页,它处理 HyperlinkClick 事件,以使被调用页实例化并向其传递初始字符串值。

Imports System.Windows ' RoutedEventArgs, RoutedEventHandler, Visibility
Imports System.Windows.Controls ' Page
Imports System.Windows.Navigation ' ReturnEventArgs

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
using System.Windows; // RoutedEventArgs, RoutedEventHandler, Visibility
using System.Windows.Controls; // Page
using System.Windows.Navigation; // ReturnEventArgs

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");


...


}


...


    }
}
<Hyperlink Name="pageFunctionHyperlink">Call Page Function</Hyperlink>

不必向被调用页传递参数。 可以执行以下操作:

但是,您不久就会看到,您仍然需要使用代码来实例化并导航到被调用页以收集被调用页返回的数据。 为此,PageFunction<T> 需要保持活动状态;否则,当您下一次导航到 PageFunction<T> 时,WPF 将使用默认构造函数来实例化 PageFunction<T>

但是,在被调用页返回之前,需要返回可以由调用页检索的数据。

将任务的任务结果和任务数据返回到调用页

在用户使用完被调用页后(在本示例中通过按 OK(确定)或 Cancel(取消)按钮来表示),被调用页需要返回。 因为调用页已使用被调用页来从用户那里收集数据,所以调用页需要两种类型的信息:

  1. 用户是否取消了被调用页(在本示例中通过按 OK(确定)或 Cancel(取消)按钮)。 这使得调用页可以确定是否处理被调用页从用户那里收集的数据。

  2. 用户提供的数据。

为了返回信息,PageFunction<T> 实现 OnReturn 方法。 下面的代码演示如何调用它。

Imports System ' String
Imports System.Windows ' RoutedEventArgs, RoutedEventHandler
Imports System.Windows.Navigation ' PageFunction

Namespace StructuredNavigationSample

Public Class CalledPageFunction
    Inherits PageFunction(Of String)


...


    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
using System; // String
using System.Windows; // RoutedEventArgs, RoutedEventHandler
using System.Windows.Navigation; // PageFunction

namespace StructuredNavigationSample
{
    public partial class CalledPageFunction : PageFunction<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);
        }
    }
}

在本示例中,如果用户按 Cancel(取消)按钮,则会向调用页返回 null 值。 如果改为按“确定”按钮,则返回用户提供的字符串值。 OnReturn 是一个 protected virtual 方法,通过调用该方法可向调用页返回数据。 数据需要打包在泛型 ReturnEventArgs<T> 类型的实例中,其类型参数指定 Result 返回的值的类型。 这样,当您使用特定的类型参数声明 PageFunction<T> 时,即指明 PageFunction<T> 将返回由类型参数指定的类型的实例。 在本示例中,类型参数以及由此而得到的返回值属于 String 类型。

当调用 OnReturn 时,调用页需要某种方法来接收 PageFunction<T> 的返回值。 为此,PageFunction<T> 实现由调用页处理的 Return 事件。 当调用 OnReturn 时,会引发 Return,因此调用页可以向 Return 注册来接收通知。

Imports System.Windows ' RoutedEventArgs, RoutedEventHandler, Visibility
Imports System.Windows.Controls ' Page
Imports System.Windows.Navigation ' ReturnEventArgs

Namespace StructuredNavigationSample

Public Class CallingPage
    Inherits Page


...


    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
using System.Windows; // RoutedEventArgs, RoutedEventHandler, Visibility
using System.Windows.Controls; // Page
using System.Windows.Navigation; // ReturnEventArgs

namespace StructuredNavigationSample
{
    public partial class CallingPage : 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;
            }
        }
    }
}

当任务完成时移除任务页

当被调用页返回,并且用户未取消被调用页时,调用页将处理由用户提供并且从被调用页返回的数据。 这种方式的数据获取通常是一个独立的活动;当被调用页返回时,调用页需要创建并导航到新的调用页来捕获更多数据。

但是,除非从日记中移除了被调用页,否则,用户将能够重新导航到调用页的上一个实例。 是否在日记中保留 PageFunction<T>RemoveFromJournal 属性决定。 默认情况下,调用 OnReturn 时会自动移除页面函数,因为 RemoveFromJournal 设置为 true。 若要在调用 OnReturn 后在导航历史记录中保留页面函数,请将 RemoveFromJournal 设置为 false。

其他类型的结构化导航

本主题举例说明了 PageFunction<T> 的最基本使用,以支持调用/返回结构化导航。 这一基础使您能够创建更复杂的结构化导航类型。

例如,有时调用页需要多个页面来从用户那里收集足够的数据或者执行任务。 多个页面的使用称为“向导”。

在其他情况下,应用程序可能具有依赖于结构化导航来有效操作的复杂导航拓扑。 有关更多信息,请参见导航拓扑概述

请参见

参考

PageFunction<T>

NavigationService

概念

导航拓扑概述