Общие сведения о переходах
Windows Presentation Foundation (WPF) поддерживает навигацию в стиле браузера, которую можно использовать в приложениях двух типов: автономных приложениях и XAML-приложениях браузера (XBAP). Чтобы упаковать содержимое для навигации WPF предоставляет класс Page. Можно переходить от одной Page к другой декларативно, с помощью Hyperlink, или программно, с помощью NavigationService. WPF использует журнал, чтобы запоминать страницы, на которые был осуществлен переход и позволяет возвращаться к ним.
Page, Hyperlink, NavigationService и журнал образуют основу для поддержки навигации, предлагаемой WPF. В этом обзоре подробно рассматриваются эти возможности, а также расширенная поддержка переходов, включающая переход к свободным XAML файлам, HTML и произвольным объектам.
Note
В этом разделе термин «браузер» относится только к браузерам, в которых можно разместить WPF-приложения, т.е. Internet Explorer и Firefox.
Переходы в приложениях WPF
В этом разделе содержится обзор основных возможностей перехода в WPF. Эти возможности доступны для автономных приложений и XBAP, но в этом разделе они представлены в контексте XBAP.
Note
В этом разделе не обсуждаются построение и развертывание XBAP. Дополнительные сведения о XBAP см. в разделе Обзор приложений браузера XAML WPF.
В этом разделе объясняются и демонстрируются следующие аспекты переходов.
Реализация страницы
В WPF вы можете перейти к нескольким типам содержимого, включая объекты .NET Framework, пользовательские объекты, значения перечисления, пользовательские элементы управления, XAML файлы и HTML файлы. Тем не менее наиболее распространенным и удобным способом упаковки содержимого является Page. Кроме того, Page реализует особые возможности перехода в целях улучшения внешнего вида приложений и упрощения их разработки.
С помощью Page можно декларативно реализовать доступную для перехода страницу XAML содержимого с помощью разметки, как показано ниже.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" />
Объект Page, реализованный в XAML, имеет корневой элемент разметки Page
и требует объявление XML-пространства имен WPF. Элемент Page
содержит содержимое, к которому необходимо осуществить переход. Для добавления содержимого задается свойство Page.Content
, как показано в следующем примере разметки.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<Page.Content>
<!-- Page Content -->
Hello, Page!
</Page.Content>
</Page>
Page.Content
может содержать только один дочерний элемент; в предыдущем примере, содержимым является отдельная строка «Hello, Page!». На практике обычно как дочерний элемент для создания и хранения содержимого используется элемент управления макетом (см. в разделе макет).
Дочерний элемент Page
считаются содержимым класса Page и, следовательно, не нужно использовать явное присвоение Page.Content
. Следующая разметка является декларативным эквивалентом предыдущего примера.
<Page xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<!-- Page Content -->
Hello, Page!
</Page>
В этом случае Page.Content
автоматически задается дочерним элементом Page
. Дополнительные сведения см. в разделе Модель содержимого WPF.
Определение Page только в разметке полезно для отображения содержимого. Тем не менее Page также может отображать элементы управления, позволяющие пользователям взаимодействовать со страницей, и он может отвечать на действия пользователя, выполняя обработку событий и вызывая логику приложения. Интерактивная Page реализуется с помощью комбинации разметки и кода программной части, как показано в следующем примере.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.HomePage">
Hello, from the XBAP HomePage!
</Page>
using System.Windows.Controls;
namespace SDKSample
{
public partial class HomePage : Page
{
public HomePage()
{
InitializeComponent();
}
}
}
Чтобы разрешить совместную работу файла разметки и файла кода программной части, требуется следующая конфигурация.
В разметке
Page
элемент должен включать атрибутx:Class
. При построении приложения существованиеx:Class
в разметке указывает Microsoft Build Engine (MSBuild) создатьpartial
класс, производный от Page и имеющий имя, заданное параметром атрибутаx:Class
. Это требует добавления в XML объявления пространства имен для схемы XAML (xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
). Созданныйpartial
класс реализуетInitializeComponent
, который вызывается для регистрации событий и задания свойств, реализованных в разметке.В коде программной части должен быть
partial
класс с тем же именем, который был задан параметром атрибутаx:Class
в разметке и он должен быть производным от Page. Это позволяет связать файл кода сpartial
классом, созданным для файла разметки при построении приложения (см. в разделе построение приложения WPF).В коде программной части класс Page должен реализовывать конструктор, который вызывает
InitializeComponent
. МетодInitializeComponent
реализуется вpartial
классе, созданном на основе реазметки, для регистрации событий и задания свойств, которые определены в разметке.
Note
При добавлении нового Page в проект, использующий Microsoft Visual Studio, Page реализуется с помощью разметки и кода программной части и включает необходимую конфигурацию для создания связи между файлами разметки и кода, как было описано здесь.
Имея созданную Page, можно осуществить переход к ней. Чтобы указать первую Page, к которой приложение переходит, необходимо настроить начальную Page.
Настройка начальной страницы
XBAP требуется определенная инфраструктура для размещения в обозревателе. В WPF класс Application является частью определения приложения, которое устанавливает необходимую инфраструктуру приложения (см. в разделе Общие сведения об управлении приложением).
Определение приложения обычно реализуется с помощью разметки и кода программной части, с файлом разметки, настроенным как элемент ApplicationDefinition
MSBuild. Ниже приведен определение приложения для XBAP.
<Application
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.App" />
using System.Windows;
namespace SDKSample
{
public partial class App : Application { }
}
Можно использовать определение приложения XBAP, чтобы указать начальную Page, которая автоматически открывается при запуске приложения. Это можно сделать, присвоив свойству StartupUri URI нужной Page.
Note
В большинстве случаев Page компилируется и развертывается вместе с приложением. В этих случаях URI, идентифицирующий Page — это pack URI, который соответствует схеме pack. Pack URI рассматриваются далее в разделе URI типа Pack в WPF. Для перехода к содержимому можно также использовать схему HTTP, которая рассматривается далее.
Можно задать StartupUri декларативно в разметке, как показано в следующем примере.
<Application
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.App"
StartupUri="PageWithHyperlink.xaml" />
В этом примере StartupUri
присваивается относительный pack URI файла HomePage.xaml
. При запуске XBAP будет автоматически осуществлен переход к HomePage.xaml. Это показано на следующем рисунке, показывающем XBAP, запущенное с веб-сервера.
Note
Дополнительные сведения о разработке и развертывании XBAP, см. в разделах Обзор приложений браузера XAML WPF и развертывание приложений WPF.
Настройка заголовка, ширины и высоты основного окна
Одна деталь, которую вы могли заметить в предыдущем примере — то, что заголовком как браузера, так и панели вкладок является URI XBAP. Этот заголовок не только слишком длинный, но также он не является ни привлекательным, ни информативным. По этой причине Page предлагает способ для изменения заголовка заданием свойства WindowTitle. Кроме того, можно настроить ширину и высоту окна браузера, задав свойства WindowWidth и WindowHeight, соответственно.
Свойства WindowTitle, WindowWidth, и WindowHeight можно задать декларативно в разметке, как показано в следующем примере.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.HomePage"
WindowTitle="Page Title"
WindowWidth="500"
WindowHeight="200">
Hello, from the XBAP HomePage!
</Page>
Результат показан на примере ниже.
Переход по гиперссылке
Типичный XBAP состоит из нескольких страниц. Самый простой способ перехода от одной страницы к другой — использование Hyperlink. Можно декларативно добавить Hyperlink на Page с помощью элемента Hyperlink
, который показан в следующем примере разметки.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
WindowTitle="Page With Hyperlink"
WindowWidth="250"
WindowHeight="250">
<Hyperlink NavigateUri="UriOfPageToNavigateTo.xaml">
Navigate to Another Page
</Hyperlink>
</Page>
Объект Hyperlink
требует следующего:
Pack URI Page для перехода, определяемый атрибутом
NavigateUri
.Содержимого, которое пользователь может щелкнуть для осуществления перехода, например текст и изображения (перечень содержимого, которое может содержать
Hyperlink
, см. в разделе Hyperlink).
На следующем рисунке показан XBAP с Page, на которую добавлен Hyperlink.
Как и следовало ожидать, щелчок по Hyperlink вызывает в XBAP переход к Page, определяемому атрибутом NavigateUri
. Кроме того, XBAP добавляет запись для предыдущего Page в список последних страниц в Internet Explorer. Это показано на следующем рисунке.
Помимо поддержки перехода от одного Page к другой, Hyperlink также поддерживает переход к фрагменту.
Переход к фрагменту
Переход к фрагменту — это переход к фрагменту содержимого на текущей или другой Page. В WPF, фрагмент содержимого представляет собой данные, содержащиеся в именованном элементе. Именованный элемент — это элемент, имеющий атрибут Name
. В следующей разметке показан именованный TextBlock
, содержащий фрагмент содержимого.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
WindowTitle="Page With Fragments" >
<!-- Content Fragment called "Fragment1" -->
<TextBlock Name="Fragment1">
Ea vel dignissim te aliquam facilisis ...
</TextBlock>
</Page>
Для перехода к фрагменту атрибут NavigateUri
элемента Hyperlink должен содержать следующее:
URI Page, к фрагменту содержимого которой нужно перейти.
Символ "#".
Имя элемента на Page, включающего фрагмент содержимого.
Фрагмент URI имеет следующий формат.
URI_страницы #
имя_элемента.
Ниже приведен пример Hyperlink
, настроенной на переход к фрагменту содержимого.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
WindowTitle="Page That Navigates To Fragment" >
<Hyperlink NavigateUri="PageWithFragments.xaml#Fragment1">
Navigate To pack Fragment
</Hyperlink>
</Page>
Note
В этом разделе описывается реализация переходов фрагмента по умолчанию в WPF. WPF, кроме того, позволяет реализовать собственную схему переходов фрагмента, которая, в частности, требует обработки событий NavigationService.FragmentNavigation.
Important
Вы можете перейти к фрагментам на свободных XAML-страницах (только для разметки XAML с корневым элементом Page
) только в том случае, если эти страницы доступны через HTTP.
Тем не менее, свободная XAML-страница может переходить к своим собственным фрагментам.
Служба переходов
Хотя Hyperlink позволяет пользователю осуществить переход к конкретному Page, работа по поиску и загрузке страницы выполняется с помощью класса NavigationService. По сути, NavigationService предоставляет возможность обработки запроса перехода со стороны клиентского кода, например от элемента Hyperlink. Кроме того, NavigationService реализует поддержку более высокого уровня для отслеживания и влияния на запросы перехода.
При нажатии Hyperlink, WPF вызывает NavigationService.Navigate для обнаружения и загрузки Page по указанному Pack URI. Загруженный Page преобразуется в дерево объектов, корневым объектом которого является загруженный экземпляр Page. Ссылка на корневой объект Page хранится в свойстве NavigationService.Content. Pack URI для содержимого, к которому был осуществлен переход, сохраняется в свойстве NavigationService.Source, при этом свойство NavigationService.CurrentSource сохраняет Pack URI для последней страницы, к которой был осуществлен переход.
Note
WPF приложение может иметь более одного активного NavigationService. Дополнительные сведения см. в разделе узлы переходов далее в этой статье.
Программный переход с помощью службы переходов
Вам не нужно знать о NavigationService, если переход реализован декларативно в разметке с помощью Hyperlink, так как Hyperlink использует NavigationService за вас. Это означает, что пока прямой или непрямой родитель объекта Hyperlink является узлом перехода (см. в разделе узлы переходов), Hyperlink будут иметь возможность находить и использовать службу переходов этого узла для обработки запросов навигации.
Тем не менее, существуют ситуации, когда необходимо использовать NavigationService напрямую, включая следующие:
Если вам нужно создать экземпляр Page с помощью конструктора не по умолчанию.
Если вам нужно задать свойства Page перед переходом к нему.
Когда Page, к которой необходимо осуществлять переход, можно определить только во время выполнения.
В этих случаях необходимо написать код для программного осуществления перехода посредством вызова метода Navigate у объекта NavigationService. Для этого требуется получить ссылку на NavigationService.
Получение ссылки на службу переходов
По причинам, описанным в разделе узлы переходов, WPF приложение может иметь более одного NavigationService. Это означает, что в коде необходимо предусмотреть способ поиска нужного NavigationService, который обычно является тем NavigationService, который привел к текущей Page. Можно получить ссылку на NavigationService путем вызова статического метода NavigationService.GetNavigationService. Чтобы получить NavigationService, который привел к конкретной Page, передайте ссылку на Page в качестве аргумента GetNavigationService. Следующий код показывает способ получения NavigationService для текущей Page.
using System.Windows.Navigation;
// Get a reference to the NavigationService that navigated to this Page
NavigationService ns = NavigationService.GetNavigationService(this);
Для быстрого поиска NavigationService, Page реализует свойство NavigationService. Его использование показано в следующем примере.
using System.Windows.Navigation;
// Get a reference to the NavigationService that navigated to this Page
NavigationService ns = this.NavigationService;
Note
Объект Page может получить только ссылку на его NavigationService только после срабатывания события Loaded.
Программный переход к объекту страницы
В следующем примере показано, как использовать NavigationService для программного перехода к Page. Программный переход является обязательным, поскольку Page, к которой выполняется переход, может быть создана только с помощью конструктора не по умолчанию с параметрами. Элемент Page с нестандартным конструктором показан в следующей разметке и коде.
<Page
x:Class="SDKSample.PageWithNonDefaultConstructor"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="PageWithNonDefaultConstructor">
<!-- Content goes here -->
</Page>
using System.Windows.Controls;
namespace SDKSample
{
public partial class PageWithNonDefaultConstructor : Page
{
public PageWithNonDefaultConstructor(string message)
{
InitializeComponent();
this.Content = message;
}
}
}
Page, которая осуществляет переход к Page с нестандартным конструктором, показана в следующей разметке и коде.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.NSNavigationPage">
<Hyperlink Click="hyperlink_Click">
Navigate to Page with Non-Default Constructor
</Hyperlink>
</Page>
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
namespace SDKSample
{
public partial class NSNavigationPage : Page
{
public NSNavigationPage()
{
InitializeComponent();
}
void hyperlink_Click(object sender, RoutedEventArgs e)
{
// Instantiate the page to navigate to
PageWithNonDefaultConstructor page = new PageWithNonDefaultConstructor("Hello!");
// Navigate to the page, using the NavigationService
this.NavigationService.Navigate(page);
}
}
}
При щелчке по Hyperlink на этой Page запускается переход путем создания экземпляра Page с помощью конструктора не по умолчанию и вызова метода NavigationService.Navigate. Метод Navigate принимает ссылку на объект для перехода, а не pack URI.
Программный переход с URI типа pack
Если вам необходимо сформировать pack URI программно (например, если pack URI можно определить только во время выполнения), можно использовать метод NavigationService.Navigate. Его использование показано в следующем примере.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.NSUriNavigationPage">
<Hyperlink Click="hyperlink_Click">Navigate to Page by Pack URI</Hyperlink>
</Page>
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
namespace SDKSample
{
public partial class NSUriNavigationPage : Page
{
public NSUriNavigationPage()
{
InitializeComponent();
}
void hyperlink_Click(object sender, RoutedEventArgs e)
{
// Create a pack URI
Uri uri = new Uri("AnotherPage.xaml", UriKind.Relative);
// Get the navigation service that was used to
// navigate to this page, and navigate to
// AnotherPage.xaml
this.NavigationService.Navigate(uri);
}
}
}
Обновление текущей страницы
Объект Page не загружается, если он имеет тот же pack URI, что и URI, хранящий в свойстве NavigationService.Source. Чтобы WPF принудительно загрузил текущую страницу, можно вызвать метод NavigationService.Refresh, как показано в следующем примере.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.NSRefreshNavigationPage">
<Hyperlink Click="hyperlink_Click">Refresh this page</Hyperlink>
</Page>
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
namespace SDKSample
{
public partial class NSRefreshNavigationPage : Page
{
void hyperlink_Click(object sender, RoutedEventArgs e)
{
// Force WPF to download this page again
this.NavigationService.Refresh();
}
}
}
Время существования перехода
Как вы уже видели, существует множество способов осуществления перехода. При вызове перехода и во время его осуществления, можно отслеживать и влиять на него с помощью следующих событий, которые реализуются NavigationService:
Navigating. Происходит, когда запрошен новый переход. Можно использовать для отмены перехода.
NavigationProgress. Происходит периодически во время загрузки, тем самым предоставляя информацию о ходе процесса навигации.
Navigated. Происходит, когда страница найдена и загружена.
NavigationStopped. Происходит, когда переход остановлен (путем вызова StopLoading), или при запросе нового перехода во время выполнения текущего перехода.
NavigationFailed. Происходит при возникновении ошибки во время перехода к запрошенному содержимому.
LoadCompleted. Происходит, когда содержимое, к которому был осуществлен переход, загружено и проанализировано и начинается его отрисовка.
FragmentNavigation. Происходит в начале перехода к фрагменту содержимого:
немедленно, если нужный фрагмент находится в текущем содержимом;
после загрузки исходного содержимого, если нужный фрагмент находится в другом содержимом.
События перехода вызываются в порядке, который показан на следующем рисунке.
В общем случае Page не связан с этими событиями. Более вероятно, что они связаны с приложением, и по этой причине эти события также вызываются с помощью класса Application:
Каждый раз, когда возникает событие NavigationService, вызывается соответствующее событие Application. Элементы Frame и NavigationWindow обеспечивают те же события, для обнаружения переходов в соответствующих областях.
В некоторых случаях Page могут заинтересовать эти события. Например Page может обрабатывать событие NavigationService.Navigating, чтобы определить необходимость отмены перехода. Эти действия показаны в следующем примере.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.CancelNavigationPage">
<Button Click="button_Click">Navigate to Another Page</Button>
</Page>
using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Navigation;
namespace SDKSample
{
public partial class CancelNavigationPage : Page
{
public CancelNavigationPage()
{
InitializeComponent();
// Can only access the NavigationService when the page has been loaded
this.Loaded += new RoutedEventHandler(CancelNavigationPage_Loaded);
this.Unloaded += new RoutedEventHandler(CancelNavigationPage_Unloaded);
}
void button_Click(object sender, RoutedEventArgs e)
{
// Force WPF to download this page again
this.NavigationService.Navigate(new Uri("AnotherPage.xaml", UriKind.Relative));
}
void CancelNavigationPage_Loaded(object sender, RoutedEventArgs e)
{
this.NavigationService.Navigating += new NavigatingCancelEventHandler(NavigationService_Navigating);
}
void CancelNavigationPage_Unloaded(object sender, RoutedEventArgs e)
{
this.NavigationService.Navigating -= new NavigatingCancelEventHandler(NavigationService_Navigating);
}
void NavigationService_Navigating(object sender, NavigatingCancelEventArgs e)
{
// Does the user really want to navigate to another page?
MessageBoxResult result;
result = MessageBox.Show("Do you want to leave this page?", "Navigation Request", MessageBoxButton.YesNo);
// If the user doesn't want to navigate away, cancel the navigation
if (result == MessageBoxResult.No) e.Cancel = true;
}
}
}
Если вы регистрируете обработчик с событием перехода из Page, как в предыдущем примере, необходимо также впоследствии отменить регистрацию обработчика событий. Если этого не сделать, могут возникнуть побочные эффекты относительно того, как WPF запоминает переходы Page с помощью журнала.
Запоминание переходов в журнале
WPF использует два стека для запоминания страниц, на которые был осуществлен переход: стек переходов назад и вперед. При переходе из текущего Page к новой Page или вперед к существующей Page, текущая Page добавляется в стек переходов назад. При переходе из текущей Page к предыдущей Page, текущая Page добавляется в стек переходов вперед. Стек "Назад", стек "Вперед" и функциональные возможности для управления ими в совокупности называются журналом. Каждый элемент в стеке переходов назад и вперед — это экземпляр класса JournalEntry и называется запись журнала.
Перемещение по журналу в браузере Internet Explorer
По существу, журнал работает так же, как и кнопки назад и вперед в Internet Explorer. Это показано на следующем рисунке.
Для XBAP, размещаемых в Internet Explorer, WPF осуществляет интеграцию журнала в области навигации UI с Internet Explorer. Это позволяет пользователям перемещаться по страницам в XBAP с помощью кнопок назад, вперед, и последние страницы в Internet Explorer. Журнал не интегрирован в Microsoft Internet Explorer 6 таким же образом, что и в Internet Explorer 7 или Internet Explorer 8. Вместо этого WPF отображает замещающий его UI навигации.
Important
В Internet Explorer, при переходе со XBAP страницы и обратно, в журнале сохраняются только записи журнала для страниц, которые не поддерживались в активном состоянии. Обсуждение поддержки страниц в активном состоянии см. в разделе время существования страницы и журнал далее в этом разделе.
По умолчанию текст для каждого Page, отображаемый в списке последние страницы Internet Explorer — URI для Page. В большинстве случаев это не особенно важно для пользователя. К счастью, можно изменить текст, используя следующие параметры.
Значение атрибута
JournalEntry.Name
.Значение атрибута
Page.Title
.Значение атрибута
Page.WindowTitle
и URI для текущей Page.URI интерфейса для текущего объекта Page. (Значение по умолчанию)
Порядок, в котором перечислены параметры, совпадает с порядком приоритета для поиска текста. Например если JournalEntry.Name
не установлен, другие значения игнорируются.
В следующем примере используется атрибут Page.Title
, чтобы изменить текст, отображаемый в записи журнала.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.PageWithTitle"
Title="This is the title of the journal entry for this page.">
</Page>
using System.Windows.Controls;
namespace SDKSample
{
public partial class PageWithTitle : Page
{
}
}
Перемещение по журналу с помощью WPF
Несмотря на то, что пользователь может перемещаться по журналу с помощью кнопок назад, вперед и последние страницы в Internet Explorer, можно также переходить по журналу с помощью декларативного и программного механизмов, предоставляемых WPF. Одна из причин для этого — предоставление пользовательских переходов UI на страницах.
Можно декларативно добавить поддержку журнального перехода с помощью команд перехода, предоставляемых NavigationCommands. Следующий пример демонстрирует, как использовать команду перехода BrowseBack
.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.NavigationCommandsPage">
<Hyperlink Command="NavigationCommands.BrowseBack">Back</Hyperlink>
<Hyperlink Command="NavigationCommands.BrowseForward">Forward</Hyperlink>
</Page>
Можно программно перемещаться по журналу с помощью одного из следующих членов класса NavigationService:
Журналом можно также управлять программным образом, как описано в разделе сохранение состояния содержимого с помощью журнала переходов далее.
Время существования страницы и журнал
Рассмотрим XBAP с несколькими страницами, которые содержат форматированное содержимое, включая графики, анимации и мультимедиа. Объем памяти для подобных страниц может быть довольно большим, особенно если используются видеоматериалы и звуковые файлы. Учитывая, что в журнале посещенные страницы XBAP запоминаются в журнале, они могут быстро израсходовать значительный объем памяти.
По этой причине, по умолчанию журнал хранит в каждой записи журнала метаданные Page, а не ссылку на объекта Page. При переходе к записи журнала метаданные Page используются для создания нового экземпляра указанной Page. Как следствие, каждая Page, к которой осуществляется переход, имеет время существования, которое показано на следующем рисунке.
Хотя при использовании поведения журнала по умолчанию можно сэкономить потребление памяти, производительность отрисовки каждой страницы может уменьшиться; повторное создание экземпляров Page может занимать много времени, особенно в том случае, если он имеет много содержимого. Если необходимо сохранить экземпляр Page в журнале, есть два способа это сделать. Во-первых, можно осуществить программный переход к Page путем вызова метода NavigationService.Navigate.
Во-вторых, можно указать, чтобы WPF сохранял экземпляр Page в журнале, задав свойству KeepAlive значение true
(по умолчанию используется false
). Как показано в следующем примере, можно задать KeepAlive декларативно в разметке.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.KeepAlivePage"
KeepAlive="True">
An instance of this page is stored in the journal.
</Page>
Время существования Page, которая поддерживается в активном состоянии, немного отличается от обычных. При первом переходе на такую Page так же создается экземпляр Page. Тем не менее, так как экземпляр Page сохраняется в журнале, он никогда не инициализируется повторно, пока он остается в журнале. Следовательно, если Page имеет логику инициализации, которая должна вызываться каждый раз при переходе, следует переместить ее из конструктора в обработчик события Loaded. Как показано на следующем рисунке, события Loaded и Unloaded по-прежнему вызываются каждый раз при переходе к Page и обратно, соответственно.
Когда Page не поддерживается в активном состоянии, можно выполнять одно из следующих:
- Сохранять ссылку или любую его часть.
- Зарегистрировать обработчики событий, которые не реализованы в объекте.
При выполнении любого из этих действий будут созданы ссылки, которые будут удерживать Page в памяти, даже в том случае, если он был удален из журнала.
В общем случае следует отдавать предпочтение поведение по умолчанию для Page, без поддержки в активном состоянии. Однако это имеет особое влияние на ее состояние, что описано в следующем разделе.
Сохранение состояния содержимого с помощью журнала переходов
Если Page не поддерживается в активном состоянии, но имеет элементы управления, которые собирают данные от пользователя, что происходит с данными, если пользователь переходит к Page и обратно? С точки зрения пользователя следует ожидать появления ранее введенных данных. К сожалению, так как новый экземпляр класса Page создается при каждом переходе, элементы управления, которые собирают данные, инициализируются заново и данные будут потеряны.
К счастью, журнал обеспечивает запоминание данных при переходах между разными Page, включая данные элементов управления. В частности, записи журнала для каждого Page действуют как временный контейнер для связанного состояния Page. Ниже показано, как используется эта поддержка при переходе с Page:
Запись для текущей Page добавляется в журнал.
Состояние Page сохраняется в записи журнала для этой страницы, которая добавляется в стек переходов назад.
Осуществляется переход к новой Page.
Если осуществляется обратный переход к Page с помощью журнала, выполняются следующие действия:
Создается Page (самая верхняя запись журнала в стеке переходов назад).
Page обновляется с использованием состояниея, которое было сохранено в записи журнала для Page.
Выполняется переход к Page.
WPF автоматически использует эту поддержку для следующих элементов управления на Page:
Если Page использует эти элементы управления, содержащиеся в них данные запоминаются при переходах между Page, как показано для ListBox Favourite color на следующем рисунке.
Когда Page имеет элементы управления, не упомянутые в предыдущем списке, или когда состояние сохраняется в пользовательских объектах, необходимо написать код для сохранения в журнале переходов состояния Page.
Если необходимо запомнить небольшие части состояния Page при переходах, можно использовать свойства зависимостей (см. в разделе DependencyProperty), настроенные с помощью флага метаданных FrameworkPropertyMetadata.Journal.
Если состояние Page, которое необходимо сохранить при переходах, состоит из нескольких фрагментов данных, можно сократить код, инкапсулировав состояние в одном классе и реализовав интерфейс IProvideCustomContentState.
Если вам необходимо перейти по различным состояниям одного Page, не переходя с Page, можно использовать IProvideCustomContentState и NavigationService.AddBackEntry.
Файлы cookie
Другой способ сохранения файлов в приложениях WPF — использование файлов cookie, которые создаются, обновляются и удаляются с помощью методов SetCookie и GetCookie. Файлы cookie в WPF аналогичны файлам cookie, используемым в других видах веб-приложений; файлы cookie представляют собой произвольные фрагменты данных, которые хранятся в приложении на клиентском компьютере во время сеансов приложения или между ними. Данные файлов cookie обычно представлены в следующем формате.
имя =
значение
При передаче данных в метод SetCookie, вместе с Uri расположения, для которого задается файл cookie, этот файл cookie создается в памяти, и он доступен только в течение текущего сеанса приложения. Этот тип файла cookie называется файл cookie сеанса.
Чтобы сохранить файл cookie на протяжении нескольких сеансов приложения, необходимо добавить в файл cookie дату окончания срока действия, используя следующий формат.
ИМЯ =
ЗНАЧЕНИЕ ; expires=DAY, DD-MMM-YYYY HH:MM:SS GMT
Файл cookie с датой окончания срока действия хранится в текущей папке временных файлов Интернета Windows до истечения его срока действия. Такой файл cookie называется постоянный файл cookie, так как он сохраняется между сеансами приложения.
Получить сеанс и постоянные файлы cookie можно, вызвав метод GetCookie, передавая в него Uri расположения, где был задан файл cookie с помощью метода SetCookie.
Ниже приведены некоторые способы поддержки файлов cookie в WPF:
Автономные приложения WPF и приложения XBAP могут создавать файлы cookie и управлять ими.
Файлы cookie, создаваемые XBAP, можно получить из браузера.
XBAP из одного домена могут создавать и совместно использовать файлы cookie.
XBAP и HTML-страницы из одного домена могут создавать и совместно использовать файлы cookie.
Файлы cookie отправляются, когда XBAP и свободные XAML-страницы отправляют веб-запросы.
И XBAP верхнего уровня, и XBAP, размещенным в IFRAME, доступны файлы cookie.
Поддержка файлов cookie в WPF одинакова для всех поддерживаемых браузеров.
В Internet Explorer, политика P3P относительно cookie соблюдается системой WPF, особенно в отношении собственных и сторонних XBAP.
Структурированная навигация
Если вам нужно передать данные из одной Page в другую, можно передать данные как аргументы конструктора не по умолчанию Page. Обратите внимание, что если вы используете этот способ, то необходимо поддерживать Page в активном состоянии; если этого не сделать, в следующий раз при переходе к Page, WPF заново создаст Page с помощью конструктора по умолчанию.
Кроме того, ваша Page может реализовать свойства, которые необходимо передать вместе с данными. Все усложняется, если Page необходимо передать данные обратно в Page, с которой был осуществлен переход. Проблема в том, что изначально переходы не поддерживают механизмы, гарантирующие, что будет осуществлен возврат к Page после перехода с него. По существу, переходы не поддерживают семантику вызова/возврата. Чтобы решить эту проблему, WPF предоставляет класс PageFunction<T>, который можно использовать, чтобы гарантировать возврат к Page в прогнозируемом и структурированном виде. Дополнительные сведения см. в разделе Общие сведения о структурированной навигации.
Класс NavigationWindow
К этому моменту мы рассмотрели целый ряд служб переходов, которые с наибольшей вероятностью будут использоваться для построения приложений с содержимым, допускающим переходы. Эти службы обсуждались в контексте приложений XBAP, несмотря на то, что они не ограничиваются XBAP. Современные автономные приложения Windows также пользуются преимуществами навигации. Вот наиболее распространенные примеры.
Тезаурус Word: Переход по вариантам слов.
Обозреватель файлов: Просмотр файлов и папок.
Мастеры: Разбиение сложной задачи на несколько страниц, которые можно перемещаться. Например, мастер компонентов Windows, который обрабатывает добавление и удаление компонентов Windows.
Для включения навигации в стиле браузера в автономных приложениях можно использовать класс NavigationWindow. NavigationWindow является производным от Window и расширяет его такой же поддержкой навигации, как и предоставляемой XBAP. Можно использовать NavigationWindow как главное окно автономного приложения или как дополнительное окно, например диалоговое.
Для реализации NavigationWindow, как и в случае с большинством классов верхнего уровня в WPF (Window, Page и так далее), используется комбинация разметки и кода. Это показано в следующем примере.
<NavigationWindow
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.MainWindow"
Source="HomePage.xaml"/>
using System.Windows.Navigation;
namespace SDKSample
{
public partial class MainWindow : NavigationWindow
{
public MainWindow()
{
InitializeComponent();
}
}
}
Этот код создает NavigationWindow, которое автоматически переходит к Page (HomePage.xaml) при открытии NavigationWindow. Если NavigationWindow является главным окном приложения, можно использовать атрибут StartupUri
, чтобы запустить его. Это показано в следующем примере разметки.
<Application
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
StartupUri="MainWindow.xaml" />
На следующем рисунке показан NavigationWindow как главное окно автономного приложения.
Из рисунка, можно увидеть, что NavigationWindow имеет заголовок, несмотря на то, что он не задан в реализации NavigationWindow. Вместо этого заголовок задается с помощью свойства WindowTitle, которое показано в следующем коде.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
Title="Home Page"
WindowTitle="NavigationWindow">
</Page>
Установка WindowWidth и WindowHeight также влияет на NavigationWindow.
Обычно вы реализуете собственный NavigationWindow при необходимости настроить его поведение или внешний вид. Если вы этого не сделали, можно использовать команду быстрого вызова. Если указать pack URI Page как StartupUri в автономном приложении, Application автоматически создает NavigationWindow для размещения Page. В следующем примере разметки показано, как это сделать.
<Application
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
StartupUri="HomePage.xaml" />
Если необходимо, чтобы дополнительным окном приложения, например диалоговым, было NavigationWindow, можно использовать код как в следующем примере.
// Open a navigation window as a dialog box
NavigationWindowDialogBox dlg = new NavigationWindowDialogBox();
dlg.Source = new Uri("HomePage.xaml", UriKind.Relative);
dlg.Owner = this;
dlg.ShowDialog();
На рисунке ниже показан результат.
Как вы видите, NavigationWindow отображает кнопки обратно и вперед в стиле Internet Explorer, которые позволяют пользователям перемещаться по журналу. Эти кнопки предоставляют пользователям возможности, показанные на следующем рисунке.
Если страницы предоставляют свою собственную поддержку перемещения по журналу и пользовательский интерфейс для этого, можно скрыть кнопки обратно и вперед, отображаемые в NavigationWindow, задав значение свойства ShowsNavigationUI равным false
.
Кроме того, можно использовать поддержку нестандартного UI в WPF для замены самого UI NavigationWindow.
Класс Frame
Как обозреватель, так и NavigationWindow представляют собой окна с содержимым для навигации. В некоторых случаях приложения имеют содержимое, которое не обязательно должно размещаться в целом окне. Такое содержимое помещается внутрь другого содержимого. Можно вставить содержимое с возможностью переходов в другое содержимое с помощью класса Frame. Frame предоставляет такую же поддержку, как и NavigationWindow и XBAP.
В следующем примере показано, как добавить Frame для Page декларативно с помощью элемента Frame
.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
WindowTitle="Page that Hosts a Frame"
WindowWidth="250"
WindowHeight="250">
<Frame Source="FramePage1.xaml" />
</Page>
Эта разметка присваивает атрибуту Source
элемента Frame
значение pack URI для Page, на котороую Frame должен первоначально перейти. На следующем рисунке показано XBAP с Page во Frame, осуществляющее переходы между несколькими страницами.
Не обязательно использовать Frame внутри содержимого Page. Также Frame часто размещается внутри содержимого Window.
По умолчанию Frame использует собственный журнал только при отсутствии другого журнала. Если Frame является частью содержимого, которое размещено внутри NavigationWindow или XBAP, Frame использует журнал, который принадлежит NavigationWindow или XBAP. Иногда, однако, Frame может потребоваться собственный журнал. Одной из причин для этого является необходимость в журнале переходов среди страниц, расположенных во Frame. Это показано на следующем рисунке.
В этом случае можно настроить Frame на использование собственного журнала установкой свойства JournalOwnership для Frame в значение OwnsJournal. Это показано в следующем примере разметки.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
WindowTitle="Page that Hosts a Frame"
WindowWidth="250"
WindowHeight="250">
<Frame Source="FramePage1.xaml" JournalOwnership="OwnsJournal" />
</Page>
На следующем рисунке показан Frame, использующий собственный журнал.
Обратите внимание, что записи журнала отображаются в области навигации UI в Frame, а не в Internet Explorer.
Note
Если Frame является частью содержимого, размещенного в Window, Frame использует собственный журнал и, следовательно, отображает собственный UI навигации.
Если вам требуется Frame с собственным журналом без отображения панели навигации UI, можно скрыть навигацию UI, присвоив NavigationUIVisibility значение Hidden. Это показано в следующем примере разметки.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
WindowTitle="Page that Hosts a Frame"
WindowWidth="250"
WindowHeight="250">
<Frame
Source="FramePage1.xaml"
JournalOwnership="OwnsJournal"
NavigationUIVisibility="Hidden" />
</Page>
Узлы переходов
Классы Frame и NavigationWindow называют узлами переходов. Узел переходов — это класс, который может осуществлять переход к содержимому и его отображение. В каждом узле переходов используются собственный NavigationService и журнал. На следующем рисунке показана основная структура узла переходов.
По сути, это позволяет NavigationWindow и Frame обеспечить такую же поддержку переходов, какую XBAP предоставляет при размещении в браузере.
Помимо использования NavigationService и журнала, узлы переходов реализуют те же члены, что и NavigationService. Это показано на следующем рисунке.
Это позволяет программировать поддержку переходов непосредственно с ними. Это можно использовать, если необходимо предоставить UI навигации для Frame, размещенного в Window. Кроме того, оба типа реализуют дополнительные, связанные с навигацией члены, включая BackStack
(NavigationWindow.BackStack, Frame.BackStack) и ForwardStack
(NavigationWindow.ForwardStack, Frame.ForwardStack), которые позволяют перебирать записи журнала в стеке переходов назад и вперед, соответственно.
Как упоминалось ранее, в приложении может существовать несколько журналов. На следующем рисунке показано пример, когда это возможно.
Переход к содержимому, отличному от страниц XAML
В этом разделе Page и XBAP использовался для демонстрирации различных возможностей переходов WPF. Тем не менее, скомпилированный в составе приложения Page не является единственным типом содержимого, к которому можно осуществить переход и XBAP — не единственный способ определения содержимого.
Как показано в этом разделе, можно также осуществлять переходы к свободным XAML файлам, HTML файлам и объектам.
Переход к свободным файлам XAML
Свободный XAML файл является файлом со следующими характеристиками:
Содержит только XAML (то есть без кода).
имеет объявление соответствующего пространства имен;
имя файла имеет расширение XAML.
Например, рассмотрим следующее содержимое, сохраненное как свободный XAML файл Person.xaml.
<!-- Person.xaml -->
<TextBlock xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<TextBlock FontWeight="Bold">Name:</TextBlock>
<TextBlock>Nancy Davolio</TextBlock>
<LineBreak />
<TextBlock FontWeight="Bold">Favorite Color:</TextBlock>
<TextBlock>Yellow</TextBlock>
</TextBlock>
Если дважды щелкнуть файл, браузер откроется, выполнит переход к содержимому и отобразит его. Это показано на следующем рисунке.
Можно отобразить свободный XAML файл, расположенный в следующих источниках:
веб-узел на локальном компьютере, в интрасети или Интернете;
общая папка Формат UNC (Universal Naming Convention).
локальный диск.
Свободный XAML файл можно добавить в Избранное браузера или сделать домашней страницей браузера.
Note
Дополнительные сведения о публикации и запуске свободных XAML страниц, см. в разделе развертывание приложений WPF.
Единственным ограничением в отношении свободного XAML является возможность размещения только содержимого, которое безопасно для запуска в режиме частичного доверия. Например Window
не может быть корневым элементом свободного XAML-файла. Дополнительные сведения см. в разделе Безопасность частичного доверия в WPF.
Переход к файлам HTML с помощью элемента управления Frame
Как можно догадаться, можно также перейти к HTML-страницам. Для этого необходимо просто предоставить URI, использующий схему http://
. Например, следующий XAML показывает Frame, осуществляющий переход к HTML-странице.
<Frame Source="http://www.microsoft.com/default.aspx" />
Переход к HTML требует специальных разрешений. Например, к нему нельзя перейти из XBAP, запущенного в песочнице безопасности частичного доверия для зоны Интернета. Дополнительные сведения см. в разделе Безопасность частичного доверия в WPF.
Переход к файлам HTML с помощью элемента управления WebBrowser
Элемент управления WebBrowser поддерживает размещение HTML документов, навигации, скриптов и управляемого кода взаимодействия. Подробные сведения о WebBrowser
см. в разделе WebBrowser.
Как и в случае Frame, переход по адресу HTML с помощью WebBrowser требует специальных разрешений. Например, из приложений с частичным доверием можно перейти только к HTML, расположенному на исходном узле. Дополнительные сведения см. в разделе Безопасность частичного доверия в WPF.
Переход к пользовательским объектам
Если у вас есть данные, которые хранятся в виде пользовательских объектов, один из способов отображения этих данных является создание Page с содержимым, привязанным к таким объектам (см. в разделе Общие сведения о привязке данных). Если не требуется создание целой страницы только для отображения объектов, то можно перейти непосредственно к ним.
Рассмотрим класс Person
, который реализуется в следующем коде.
using System.Windows.Media;
namespace SDKSample
{
public class Person
{
string name;
Color favoriteColor;
public Person() { }
public Person(string name, Color favoriteColor)
{
this.name = name;
this.favoriteColor = favoriteColor;
}
public string Name
{
get { return this.name; }
set { this.name = value; }
}
public Color FavoriteColor
{
get { return this.favoriteColor; }
set { this.favoriteColor = value; }
}
}
}
Для перехода к нему вызовите метод NavigationWindow.Navigate, как показано в следующем примере кода.
<Page
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="SDKSample.HomePage"
WindowTitle="Page that Navigates to an Object">
<Hyperlink Name="hyperlink" Click="hyperlink_Click">
Navigate to Nancy Davolio
</Hyperlink>
</Page>
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
namespace SDKSample
{
public partial class HomePage : Page
{
public HomePage()
{
InitializeComponent();
}
void hyperlink_Click(object sender, RoutedEventArgs e)
{
Person person = new Person("Nancy Davolio", Colors.Yellow);
this.NavigationService.Navigate(person);
}
}
}
На рисунке ниже показан результат.
Из этого рисунка можно видеть, что ничего полезного не отобразилось. На самом деле, отображаемое значение — это значение, которое возвращает метод ToString
объекта Person; по умолчанию это единственное значение, которое WPF может использовать для представления объекта. Можно переопределить метод ToString
для возврата более значимой информации, хотя это по-прежнему будет всего лишь строковое значение. Одним из методов, использующих преимущества возможностей представления WPF, является использование шаблона данных. Можно реализовать шаблон данных и связать его с объектом определенного типа. В следующем коде показан шаблон данных для объекта Person
.
<Application
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.App"
StartupUri="HomePage.xaml">
<Application.Resources>
<!-- Data Template for the Person Class -->
<DataTemplate DataType="{x:Type local:Person}">
<TextBlock xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<TextBlock FontWeight="Bold">Name:</TextBlock>
<TextBlock Text="{Binding Path=Name}" />
<LineBreak />
<TextBlock FontWeight="Bold">Favorite Color:</TextBlock>
<TextBlock Text="{Binding Path=FavoriteColor}" />
</TextBlock>
</DataTemplate>
</Application.Resources>
</Application>
Здесь шаблон данных связан с типом Person
с помощью расширения разметки x:Type
в атрибуте DataType
. Затем шаблон данных привязывает элемент TextBlock
(см. в разделе TextBlock) к свойствам класса Person
. На следующем рисунке показан обновленный внешний вид объекта Person
.
Преимуществом этого способа является связность, которая обеспечивается возможностью повторного использования шаблона данных для согласованного отображения объектов в любом месте приложения.
Дополнительные сведения о шаблонах данных см. в разделе Общие сведения о шаблонах данных.
Безопасность
Поддержка навигации WPF позволяет XBAP осуществлять переходы через Интернет, а также позволяет приложениям размещать стороннее содержимое. Для защиты приложений и пользователей WPF предоставляет широкий набор функций безопасности, которые рассматриваются в разделах безопасность WPF и безопасность частичного доверия WPF.