结构化导航概述

可由 XAML 浏览器应用程序(XBAP)、FrameNavigationWindow 托管的内容由页面组成,这些页面可以通过包统一资源标识符(URI)进行标识,并可通过超链接进行导航。 页面的结构及其导航方式(由超链接定义)称为导航拓扑。 此类拓扑适用于各种应用程序类型,尤其是浏览文档的应用程序类型。 对于此类应用程序,用户可以从一个页面导航到另一个页面,而无需了解其他任何页面。

应用程序的其他类型页面需要知道用户何时在不同页面之间导航。 例如,考虑一个人力资源应用程序,该应用程序有一页可列出组织中的所有员工,即“列出员工”页面。 此页面还可以允许用户通过单击超链接来添加新员工。 单击后,页面将导航到“添加员工”页以收集新员工的详细信息,并将其返回到“列出员工”页以创建新员工并更新该列表。 这种导航样式类似于调用方法来执行某些处理并返回一个称为结构化编程的值。 因此,这种导航样式称为 结构化导航

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

结构化导航

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

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

  • 当用户完成使用“呼叫页面”时,“被呼叫页面”会专门返回到“呼叫页面”,这一步是可选的:

    • 返回描述呼叫页面完成方式的状态信息(例如,用户按下了“确定”按钮还是“取消”按钮)。

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

  • 当调用页返回到被调用的页面时,将从导航历史记录中删除被调用的页面,以将调用页面的一个实例与另一个实例隔离开来。

下图说明了这些行为:

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

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

使用 PageFunction 进行结构化导航

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

创建通话页面

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

<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

创建要调用的页面函数

由于调用页可以使用调用页从用户收集和返回数据,因此作为泛型类实现, PageFunction<T> 其类型参数指定调用页将返回的值的类型。 以下代码展示了调用页的初始实现,该实现使用PageFunction<T>并返回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。 如代码示例所示,类型参数在 XAML 标记(使用 x:TypeArguments 特性)和代码隐藏(使用标准泛型类型参数语法)中指定。

无需仅使用 .NET Framework 类作为类型参数。 可以调用 A PageFunction<T> 来收集抽象为自定义类型的领域特定数据。 以下代码演示如何使用自定义类型作为类型 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> 的类型参数为调用页面和被调用页面之间的通信提供了基础,以下各节对此进行了讨论。

正如你所见,PageFunction<T> 的声明所识别的类型在将数据从 PageFunction<T> 返回到调用页时起着重要作用。

调用 PageFunction 并传递参数

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

以下代码显示调用页,其中包含一个非无参数构造函数,用于接受来自调用页的参数。

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

以下代码演示了调用页处理 Click 的事件,以实例化被调用页面 Hyperlink,并向其传递一个初始字符串值。

<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

无需将参数传递给调用页。 相反,您可以执行以下操作:

但是,正如你很快就会看到的那样,你仍然需要使用代码来实例化并导航到被调用的页面来收集被调用页面返回的数据。 因此,PageFunction<T> 需要保持活动状态;否则,下次导航到 PageFunction<T> 时,WPF 将使用无参数构造函数实例化 PageFunction<T>

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

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

用户在使用完调用的页面后,需要按下“确定”或“取消”按钮,此时调用的页面需要返回。 由于呼叫页使用调用页从用户收集数据,调用页需要两种类型的信息:

  1. 用户是否取消了被调用的页面(通过按本示例中的“确定”按钮或“取消”按钮)。 这样,呼叫页就可以确定是否处理从用户收集的调用页的数据。

  2. 用户提供的数据。

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

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 返回到调用页。 如果改为按下“确定”按钮,则返回用户提供的字符串值。 OnReturn 是一个 protected virtual 方法,你调用它以将数据返回到调用的页面。 数据需要打包在泛型 ReturnEventArgs<T> 类型的实例中,其类型参数指定返回的值 Result 的类型。 这样,当你声明一个具有特定类型参数的 PageFunction<T> 时,你是在表明一个 PageFunction<T> 会返回由该类型参数指定的类型的实例。 在此示例中,类型参数,因此返回值的类型为类型 String

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

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

任务完成时删除任务页

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

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

其他类型的结构化导航

本主题演示了使用 PageFunction<T> 支持调用与返回的结构化导航的最基本用法。 此基础使你能够创建更复杂的结构化导航类型。

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

在其他情况下,应用程序可能具有复杂的导航拓扑,这些拓扑依赖于结构化导航来有效运行。 有关详细信息,请参阅 导航拓扑概述

另请参阅