Страничные интерфейсы

81

Чтобы создать страничное приложение в WPF, нужно перестать применять для пользовательских интерфейсов в качестве контейнера высшего уровня класс Window и вместо него переключиться на класс System.Windows.Controls.Page.

Модель для создания страниц в WPF во многом похожа на модель для создания окон. Хотя создавать объекты страниц можно и с помощью одного лишь кода, обычно для каждой страницы создается файл XAML и файл отделенного кода. При компиляции этого приложения компилятор создает производный класс страницы, который объединяет написанный разработчиком код с генерируемыми автоматически связующими элементами (такими как поля, которые ссылаются на каждый именованный элемент на странице).

Страницу можно добавлять в любой проект WPF. Для этого в Visual Studio нужно выбрать в меню Project (Проект) пункт Add Page (Добавить страницу). Хотя страницы и являются самым высокоуровневым компонентом пользовательского интерфейса при проектировании приложения, во время его выполнения контейнером наивысшего уровня они уже не будут. Вместо этого они обслуживаются в другом контейнере. Именно в этом и состоит секрет гибкости, обеспечиваемой WPF в случае страничных приложений, ведь в качестве такого контейнера WPF позволяет использовать любой из нескольких следующих объектов:

В качестве примера простейшего страничного приложения давайте создадим следующую страницу:

<Page x:Class="WpfApplication1.Page1"
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
      Title="Page1" WindowTitle="Page1">
    <StackPanel Margin="5">
        <Label Margin="5">Пример обычной страницы</Label>
        <Button Margin="5,0,5,0" Padding="5">OK</Button>
        <Button Padding="5" Margin="5">Close</Button>
    </StackPanel>
</Page>

Теперь изменим содержимое файла App.xaml так, чтобы в качестве начальной страницы использовался файл этой страницы:

<Application x:Class="WpfApplication1.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             StartupUri="Page1.xaml">
    <Application.Resources></Application.Resources>
</Application>

При запуске этого приложения среде WPF хватит "интеллектуальных способностей", чтобы понять, что указывается страница, а не окно. Она автоматически создаст новый объект NavigationWindow для выполнения роли контейнера и отобразит страницу внутри него. Она также считает свойство WindowTitle и использует его значение в качестве заголовка окна:

Страница в контейнере NavigateWindow

Одно из отличий между страницей и окном заключается в том, что размер страницы обычно не устанавливается, поскольку он определяется обслуживающим ее контейнером (хостом). Если же для свойств Width и Height страницы все-таки указаны какие-то значения, страница делается именно такого размера, но часть ее содержимого может быть усечена, если размер окна хоста оказывается меньше, или размещена по центру доступного пространства, если его размер больше.

Объект NavigationWindow более или менее похож на обычное окно, за исключением кнопок навигации "вперед" и "назад", которые отображаются в строке сверху. Поэтому нетрудно догадаться, что класс NavigationWindow унаследован от класса Window и имеет небольшой дополнительный набор связанных с навигацией свойств. Извлечь ссылку на содержащий объект NavigationWindow можно с помощью следующего кода:

// Извлечение ссылки на окно, содержащее текущую страницу
NavigationWindow win = (NavigationWindow)Window.GetWindow(this);

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

По возможности подхода с применением NavigationWindow лучше вообще избегать и использовать вместо него свойства класса Page (и службу навигации). Иначе страница будет тесно связана с контейнером NavigationWindow, и потому ее нельзя будет использовать повторно в других хостах.

При желании создать приложение, состоящее только из кода, для достижения эффекта, показанного на рисунке выше, потребовалось бы создать как страницу, так и навигационное окно. Код, который пришлось бы для этого использовать, показан ниже:

NavigationWindow win = new NavigationWindow()
win.Content = new Page1();
win.Show();

Класс Page

Подобно Window, класс Page допускает наличие только единственного вложенного элемента. Однако класс Page не является элементом управления содержимым: он на самом деле унаследован непосредственно от класса FrameworkElement. Вдобавок класс Page является более простым и отлаженным, чем класс Window. Он имеет небольшой набор дополнительных свойств, которые позволяют настраивать его внешний вид, взаимодействовать с контейнером только определенным, ограниченным образом и применять навигацию. Все эти свойства перечислены ниже:

Background

Принимает кисть, которая позволяет устанавливать заливку для фона

Content

Принимает один элемент, который отображается на странице. Обычно в роли такого элемента выступает контейнер компоновки, такой как Grid или StackPanel

Foreground, FontFamily и FontSize

Определяют внешний вид по умолчанию для текста внутри страницы. Значения этих свойств наследуются элементами внутри страницы. Например, если устанавливается заливка переднего плана и размер шрифта, по умолчанию содержимое внутри страницы получает эти же настройки

WindowWidth, WindowHeight и WindowTitle

Определяют внешний вид окна, в которое упаковывается страница. Эти свойства позволяют управлять хостом путем установки его ширины, высоты и заголовка. Однако они действуют только в том случае, если страница обслуживается в окне (а не во фрейме)

NavigationService

Возвращает ссылку на объект NavigationService, которую можно использовать для отправки пользователя на другую страницу программным путем

KeepAlive

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

ShowsNavigationUI

Определяет, должен ли хост для данной страницы отображать навигационные элементы управления (кнопки "назад" и "вперед"). По умолчанию имеет значение true

Title

Устанавливает имя, которое должно применяться для страницы в хронологии навигации. Хост не использует свойство Title для установки заголовка в строке заголовка: для этой цели у него есть свойство WindowTitle

Также важно обратить внимание на отсутствующие компоненты — в классе Page нет эквивалентов для методов Hide() и Show(), доступных в классе Window. Если потребуется показать другую страницу, придется воспользоваться навигацией.

Пройди тесты
Лучший чат для C# программистов