Общие сведения о структурной навигации

Содержимое, которое может быть размещено приложением браузера XAML (XBAP), Frame или NavigationWindow, состоит из страниц, которые можно идентифицировать универсальными кодами ресурса (URI) пакета и на которые можно перейти по гиперссылкам. Структура страниц и способы навигации по ним с помощью гиперссылок называется топологией навигации. Такая топология подходит для различных типов приложений, особенно тех, в которых необходим переход по документам. В таких приложениях пользователь может перемещаться с одной страницы на другую даже при отсутствии связи между ними.

Тем не менее другие типы приложений содержат страницы, которым необходимо ссылаться друг на друга. Например, рассмотрим приложение управления персоналом, в котором есть одна страница со списком всех сотрудников организации — страница "Список сотрудников". На этой странице пользователи также могут добавить нового сотрудника, щелкнув гиперссылку. При ее щелчке открывается новая страница "Добавить сотрудника", на которой собираются сведения о новом сотруднике и возвращаются на страницу "Список сотрудников". При этом создается новый сотрудник и обновляется список. Этот стиль навигации аналогичен вызову метода для выполнения обработки и возврата значения, который называется структурным программированием. Таким образом, этот стиль навигации называется структурной навигацией.

Класс Page не реализует поддержку структурной навигации. Вместо этого класс PageFunction<T> наследуется от Page и дополняет ее базовыми конструкциями, необходимыми для структурной навигации. В этом разделе показано установление структурной навигации с помощью PageFunction<T>.

Структурная навигация

Когда одна страница вызывает другую в структурной навигации, необходимы некоторые или все из следующих видов поведения.

  • Переход выполняется с вызывающей страницы на вызываемую. При этом могут передаваться параметры, необходимые вызываемой странице.

  • Когда пользователь завершает работу с вызывающей страницей, вызванная страница может возвращать вызывающей странице следующее:

    • Возврат информации о состоянии, которая описывает, как была завершена работа с вызывающей страницей (например, пользователь нажал кнопку "ОК" или "Отмена").

    • Возврат данных, которые были получены от пользователя (например, сведения о новом сотруднике).

  • Когда вызывающая страница возвращается к вызываемой, вызванная страница удаляется из журнала навигации, чтобы изолировать один экземпляр вызываемой страницы от другого.

Эти модели поведения иллюстрируются следующим рисунком.

Screenshot shows the flow between calling page and called page.

Чтобы реализовать эти виды поведения, можно использовать PageFunction<T> в виде вызываемой страницы.

Структурная навигация с помощью PageFunction

В этом разделе показано, как реализовать основной механизм структурной навигации с одним PageFunction<T>. В этом примере Page вызывает PageFunction<T>, чтобы получить значение String от пользователя и вернуть его.

Создание вызывающей страницы

Страница, вызывающая PageFunction<T>, может быть либо Page, либо PageFunction<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 в качестве аргументов типа. Функцию 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

Необязательно передавать параметры вызываемой странице. Вместо этого можно сделать следующее.

  • Из вызывающей страницы:

    1. Создайте экземпляр вызываемой PageFunction<T> с помощью конструктора без параметров.

    2. Сохраните параметры в Properties.

    3. Перейдите к вызываемой PageFunction<T>.

  • С вызываемой PageFunction<T>:

    • Извлеките и используйте параметры, сохраненные в Properties.

Однако вскоре вы увидите, что и в этом случае потребуется код для создания экземпляра и перехода к вызываемой странице для сбора данных, возвращаемых вызываемой страницей. По этой причине 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

Удаление страниц задачи после завершения задачи

Когда вызываемая страница возвращается и пользователь не отменяет ее, вызывающая страница будет обрабатывать данные, предоставленные пользователем и возвращенные вызываемой страницей. Сбор данных таким способом, как правило, является изолированным действием. Когда возвращается вызываемая страница, вызывающей странице необходимо создать новую вызывающую страницу и перейти к ней для сбора дополнительных данных.

Однако, если вызываемая страница не удалена из журнала, пользователь сможет перейти обратно к предыдущему экземпляру вызывающей страницы. Свойство RemoveFromJournal определяет, сохраняется ли PageFunction<T> в журнале. По умолчанию функция страницы автоматически удаляется при вызове OnReturn, потому что для RemoveFromJournal задано значение true. Чтобы сохранить функцию страницы в истории навигации после вызова OnReturn, задайте для RemoveFromJournal значение false.

Другие типы структурной навигации

В этом разделе показан самый простой способ использования PageFunction<T> для поддержки структурной навигации вызова/возврата. На этой основе можно создавать более сложные типы структурной навигации.

Например, иногда вызывающей странице требуется несколько страниц для сбора достаточного количества данных от пользователя или выполнения задачи. Использование нескольких страниц называется "мастер".

В других случаях для эффективной работы приложения могут иметь сложные топологии переходов, зависящие от структурной навигации. Дополнительную информацию см. в разделе Общие сведения о топологиях навигации.

См. также