Опубликован: 13.12.2011 | Уровень: для всех | Доступ: платный
Лекция 8:

Использование событий, команд и триггеров в технологиях WPF и Silverlight

Аннотация: Разбирается способ привязки поведения сущностей к средствам пользовательского интерфейса WPF и Silverlight. Описывается интерфейс ICommand.

Цель лекции: команды представляют собой механизм ввода в WPF и Silverlight, обеспечивающий обработку ввода на более семантическом уровне по сравнению с устройствами ввода. Примерами команд являются операции Копировать, Вырезать и Вставить, доступные во многих приложениях. В этом разделе представлены общие сведения о командах, классах, входящих в состав модели команд, а также о порядке использования и создания событий, команд и триггеров в приложениях.

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

Часто мы хотим, чтобы приложение определенным образом реагировало на движения мышью, нажатия кнопок или клавиш. На платформе Windows Presentation Foundation есть три способа работы с действиями: события, команды и триггеры. В данной лекции мы познакомимся с принципами, применимыми ко всем этим механизмам, а затем углубимся в детали каждого в отдельности.

Принципиальные основы действий

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

Композиция элементов

Если взглянуть на устройство кнопки, то на самом деле мы увидим, что она составлена из нескольких элементов. Из-за наличия подобной композиции возникает интересная проблема при обработке событий. Памятуя о простоте модели программирования, мы хотим, чтобы код подписки на событие нажатия кнопки был прост и хорошо знаком разработчикам. Мы не должны требовать ничего сверх обычного присоединения обработчика к событию Click:

Button b = new Button();
b.Content = "Click Me";
b.Click += delegate { MessageBox.Show("You clicked me!"); };

На первый взгляд, выглядит замечательно, но посмотрим, что тут реально происходит. Ведь щелкаем мы не по кнопке, а по какому-то из элементов, из которых кнопка составлена. Чтобы все работало гладко, в WPF введено понятие маршрутизации события, которое позволяет обойти составляющие элементы. Продемонстрировать его мы сможем, добавив в предыдущем примере вторую кнопку, которая будет выступать в качестве содержимого. Щелчок как по первой, так и по второй кнопке приводит к возбуждению события:

Button b = new Button();
b.Content = new Button();
b.Click += delegate { MessageBox.Show("You clicked me!"); };

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

Дерево отображения (слева) кнопки, в которую вложена другая кнопка (справа)

Рис. 11.1. Дерево отображения (слева) кнопки, в которую вложена другая кнопка (справа)

Композиция элементов отражается на всех аспектах обработки действий, а не только на событиях.

Слабая связь

Из описания событий в интерфейсе класса Button следует, что он поддерживает не только непосредственные события мыши (MouseUp, MouseDown и т.д.), но и событие Click, которое является абстракцией гораздо более высокого уровня, чем просто событие мыши. Оно возникает и тогда, когда пользователь нажимает пробельную клавишу (при условии, что кнопка владеет фокусом) или клавишу Enter (если это кнопка по умолчанию для окна). Нажатие кнопки – это семантическое событие, тогда как события мыши относятся к физическим.

У написания кода для обработки именно события Click есть два преимущества:

  • мы не привязываемся к конкретному жесту пользователя (операции с мышью или клавиатурой);
  • мы не ограничиваем себя только кнопкой.

Каждый из элементов CheckBox, RadioButton, Button и Hyperlink поддерживает нажатие. Если обработчик связан с событием Click, то он применим к любому компоненту, который может быть "нажат". Такое отделение кода от действия обеспечивает большую гибкость в реализации обработчиков. Однако самим событиям присуща некая форма зависимости; требуется, чтобы метод-обработчик имел вполне определенную сигнатуру. Например, делегат для обработки события Button.Click определен следующим образом:

public delegate void RoutedEventHandler(object sender,RoutedEventArgs e);

Одна из целей WPF – поддержать широкий спектр действий: от тесно связанных физических событий (например, MouseUp) до чисто семантических извещений (например, команды ApplicationCommands.Close, которая сигнализирует о том, что окно должно быть закрыто).

Допуская слабую связь, мы получаем возможность писать шаблоны, которые радикально меняют элемент управления. Например, включив кнопку, ассоциированную с командой Close, мы сможем написать шаблон для окна, который добавляет элемент для закрытия:

<ControlTemplate TargetType=’{x:Type Window}’>
    <DockPanel>
        <StatusBar DockPanel.Dock=’Bottom’>
            <StatusBarItem>
                <Button Command=’{x:Static ApplicationCommands.Close}’>
                    Close
                 </Button>
            </StatusBarItem>
        </StatusBar>
        <ContentPresenter />
    </DockPanel>
</ControlTemplate>

Теперь можно заставить окно закрываться, когда любой компонент пошлет команду Close; для этого достаточно добавить в код класса окна соответствующую привязку:

public MyWindow() 
{
InitializeComponent();
CommandBindings.Add(
     new CommandBinding(ApplicationCommands.Close,CloseExecuted));
}
void CloseExecuted(object sender, ExecuteRoutedEventArgs e) 
{
    this.Close();
}

Команды – это наименее связанная модель действий в WPF. Слабая связь обеспечивает полное абстрагирование от источника действия (в данном случае кнопки) и от обработчика действия (в данном случае окна). Мы могли бы изменить стиль окна, воспользовавшись совершенно другим элементом управления, и при этом приложение продолжало бы функцианировать.

Декларативные действия

Мы видим, что с появлением команд и слабой связи WPF движется в направлении модели, когда программа просто объявляет о своих пожеланиях (например, "я хочу, чтобы окно закрылось, когда вы отдадите эту команду") вместо реализации (например, "вызвать метод Window.Close() при нажатии этой кнопки").

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

Все механизмы работы с действиями в той или иной мере поддерживают перечисленные выше принципы. Углубленное изучение действий мы начнем с наиболее известного: событий.

Анисимов Михаил
Анисимов Михаил
Украина
Наталия Шаститко
Наталия Шаститко
Украина, Днепропетровск, Днепропетровский Гуманитарный Университет, 2014