Команды
66WPF --- Привязка, команды и стили WPF --- Команды
В Windows Presentation Foundation предлагается поддержка независимых от элементов управления событий через управляющие команды (control commands). Обычное событие .NET определяется внутри некоторого базового класса и может быть использовано этим классом и его наследниками. Таким образом, нормальные события .NET очень тесно связаны с классом, в котором они определены.
В отличие от этого, управляющие команды WPF представляют собой подобные событиям сущности, которые независимы от конкретного элемента управления и во многих отношениях могут успешно применяться к многочисленным (и внешне несвязанным) типам элементов управления. Приведем несколько примеров: WPF поддерживает команды Сору (Копировать), Paste (Вставить) и Cut (Вырезать), которые могут применяться к широкому разнообразию элементов пользовательского интерфейса (пунктам меню, кнопкам панели инструментов, специальным кнопкам), а также клавиатурные комбинации (<Ctrl+C>, <Ctrl+V> и т.д.).
Хотя другие наборы инструментов для построения пользовательских интерфейсов (вроде Windows Forms) предлагают для этих целей стандартные события, в результате получается избыточный и трудный в сопровождении код. В модели WPF команды могут использоваться в качестве альтернативы. В результате получается более компактный и гибкий код.
В хорошо спроектированном Windows-приложении прикладная логика находится не в обработчиках событий, а закодирована в высокоуровневых методах. Каждый из этих методов представляет одну решаемую приложением "задачу". Каждая задача может полагаться на дополнительные библиотеки (вроде отдельно компилируемых компонентов, в которых инкапсулируется бизнес-логика или доступ к базам данных). Пример таких отношений показан на рисунке:
Наиболее очевидным способом использования такого проектного решения является добавление обработчиков событий везде, где они нужны, и применение каждого из них для вызова соответствующего метода приложения. По сути, в таком случае код окна превращается в облегченную коммутационную панель, которая реагирует на ввод и пересылает запросы внутрь приложения.
Хотя это решение вполне разумно, онo не позволяет сэкономить на кодировании. Многие задачи приложения могут инициироваться по различным маршрутам, из-за чего часто все равно приходится писать несколько обработчиков событий, вызывающих один и тот же метод приложения. Именно в этом нет особой проблемы (потому что код коммутационной панели прост), но жизнь гораздо усложняется, когда приходится иметь дело с состоянием пользовательского интерфейса.
Понять, о чем идет речь, поможет простой пример. Предположим, что есть программа, в состав которой входит метод по имени PrintDocument(). Этот метод может инициироваться четырьмя способами: через главное меню (выбором пункта меню File --> Print (Файл --> Печать)), через контекстное меню (щелчком правой кнопкой мыши в пустой области и выбором в контекстном меню пункта Print (Печать)), с помощью клавиатурной комбинации (<Ctrl+P>) и посредством соответствующей кнопки панели инструментов.
В определенных точках жизненного цикла приложения задача PrintDocument() должна быть временно недоступной. Это подразумевает отключение соответствующих команд в двух меню и кнопки в панели инструментов так, чтобы на них нельзя было выполнять щелчок, а также игнорирование клавиатурной комбинации <Ctrl+P>. Написание в данном случае всего необходимого кода — очень непростая проблема. Даже еще хуже то, что ошибки в нем могут привести к тому, что различные блоки кода состояния будут перекрываться некорректно, оставляя элемент управления в активном состоянии даже тогда, когда он не должен быть доступен. Написание и отладка подобного кода является одним из наименее приятных аспектов разработки Windows-приложений.
К удивлению многих опытных разработчиков Windows-приложений, в наборе инструментальных средств Windows Forms не было функциональности, которая могла бы облегчить решение подобных задач. Разработчики могли создавать необходимую инфраструктуру самостоятельно, но большинство из них предпочитало этого не делать. К счастью, WPF заполняет этот пробел, предлагая новую модель команд, в которой предоставляются два ключевых средства:
делегирование событий надлежащим командам;
поддержание включенного состояния элемента управления в синхронизированном виде с помощью состояния соответствующей команды.
Предлагаемая в WPF модель команд является не настолько прямолинейной, как можно было ожидать. Для подключения к модели маршрутизируемых событий ей требуется несколько отдельных компонентов, о которых речь пойдет позже. Однако в концептуальном плане она достаточно проста. На рисунке ниже показано, как построение приложения на основе команд позволяет изменить проектное решение, показанное на рисунке выше:
Теперь каждое действие, которое инициирует печать (т.е. щелчок на соответствующей кнопке либо элементе меню и нажатие клавиатурной комбинации <Ctrl+P>), отображается на одну и ту же команду. Эта команда с помощью привязки соединяется в коде со всего лишь одним обработчиком событий. Предлагаемая в WPF система команд представляет собой замечательное средство для упрощения проектирования приложений. Однако в ней все равно имеются кое-какие серьезные пробелы. В частности, WPF не поддерживает:
отслеживание команд (например, хронологию выполнявшихся команд);
невыполнимые команды;
команды, которые обладают состоянием и могут находиться в различных режимах (такие как команда, которая может быть включена и отключена).