Выполнение команд

40

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

Как объяснялось ранее, объект RoutedUICommand не имеет никакой жестко закодированной функциональности. Он просто представляет команду. Для активизации этой команды необходим источник команды (или специальный код), а для ответа на нее — привязка команды, которая переадресует ее выполнение обычному обработчику событий. Об этих двух компонентах и пойдет речь ниже.

Источники команд

Команды в библиотеке команд доступны всегда. Самый простой способ их инициирования предусматривает их привязку к элементу управления, реализующему интерфейс ICommandSource, к числу которых относятся все элементы управления, унаследованные от ButtonBase (Button, CheckBox и т.д.), индивидуальные объекты ListBoxItem, элемент управления Hyperlink и элемент управления MenuItem.

Интерфейс ICommandSource определяет три свойства, которые описаные ниже:

Command

Указывает на связанную команду. Это единственная обязательная деталь

CommandParameter

Предоставляет любые данные, которые должны отправляться вместе с командой

CommandTarget

Идентифицирует элемент, на котором должна выполняться команда

Например, ниже показан код кнопки, которая связывается с командой ApplicationCommands.New посредством свойства Command:

<Button Command="New">New</Button>

Привязки команд

После присоединения команды к источнику команды происходит нечто интересное: источник команды автоматически отключается.

Например, после создания кнопки New (Создать), она появляется как затененная и недоступная для щелчка, как будто ее свойство IsEnabled установлено в false:

Команда без привязки

Это объясняется тем, что кнопка запрашивает состояние команды, а поскольку команда не имеет присоединенной привязки, предполагается, что она отключена. Чтобы изменить такое положение дел, для команды понадобится создать привязку, которая указывает три следующие вещи:

Ниже показан фрагмент кода, в котором создается привязка для команды New. Этот код можно поместить в конструктор окна:

// Создание привязки
CommandBinding bind = new CommandBinding(ApplicationCommands.New);

// Присоединение обработчика событий
bind.Executed += NewCommand_Executed;

// Регистрация привязки
this.CommandBindings.Add(bind);

Обратите внимание, что готовый объект CommandBinding добавляется в коллекцию CommandBindings содержащего окна. Это работает через механизм пузырькового распространения событий. По сути, когда на кнопке выполняется щелчок, событие CommandBinding.Executed поднимается подобно пузырьку из кнопки к содержащим ее элементам-контейнерам.

Хотя обычно все привязки добавляются в окно, свойство CommandBindings в действительности определено базовым классом UIElement. Это означает, что оно поддерживается любым элементом. Например, приведенный выше пример будет работать точно также даже в случае добавления привязки команды и прямо в код кнопки, использующей эту команду (хотя тогда ее не удастся применить для какого-то другого элемента более высокого уровня). Для достижения наибольшей гибкости привязки команд обычно добавляются в окно наивысшего уровня. Если одну и ту же команду необходимо использовать в нескольких окнах, привязки должны быть созданы в каждом из них.

Можно также обработать событие CommandBinding.PreviewExecuted, которое сначала возбуждается в контейнере наивысшего уровня (т.е. окне) и затем туннелируется до уровня кнопки. Если установить свойство RoutedEventArgs.Handled в true, событие Executed никогда не возникнет.

В предыдущем коде предполагается, что в том же самом классе имеется готовый к получению команды обработчик событий по имени NewCommand_Executed. Ниже показан пример простого кода для отображения источника команды:

private void NewCommand_Executed(object sender, ExecutedRoutedEventArgs e)
{
     MessageBox.Show("Команда запущена из "+e.Source.ToString());
}

Теперь после запуска приложения кнопка будет отображаться как доступная. Щелчок на ней приводит к генерации события Executed, которое затем будет подниматься до уровня окна и обрабатываться показанным ранее обработчиком NewCommand_Executed(). Здесь WPF сообщит источник генерации события (кнопка).

Объект ExecutedRoutedEventArgs также позволяет извлекать ссылку на команду, которая была вызвана (ExecutedRoutedEventArgs.Command), и любую дополнительную информацию, переданную вместе с ней (ExecutedRoutedEventArgs.Parameter). В рассматриваемом примере передача дополнительной информации не предусмотрена, поэтому значением ExecutedRoutedEventArgs.Parameter будет null. (Чтобы передать дополнительную информацию, нужно установить свойство CommandParameter источника команды. Чтобы передать часть информации из другого элемента управления, это свойство должно быть установлено с использованием выражения привязки.)

Команда с привязкой

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

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

<Window.CommandBindings>
     <CommandBinding Command="New" Executed="NewCommand_Executed"></CommandBinding>
</Window.CommandBindings>

К сожалению, в Visual Studio отсутствует поддержка определения привязок команд на этапе проектирования. Кроме того, предоставляется относительно слабая поддержка для подключения элементов управления и команд. Свойство Command для элемента управления можно устанавливать в окне Properties (Свойства), но при этом приходится вводить точное имя команды — никакого удобного раскрывающегося списка возможных вариантов команд здесь не предусмотрено.

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