Поделиться через


Общие сведения о системе команд

Обновлен: Ноябрь 2007

Команды представляют собой механизм ввода в Windows Presentation Foundation (WPF), обеспечивающий обработку ввода на более семантическом уровне по сравнению с устройствами ввода. Примерами команд являются операции Копировать, Вырезать и Вставить, доступные во многих приложениях.

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

В этом разделе содержатся следующие подразделы.

  • Что представляют собой команды?
  • Простой пример команды в WPF
  • Четыре основных понятия в системе команд WPF
  • Библиотека команд
  • Создание пользовательских команд
  • Связанные разделы

Что представляют собой команды?

Система команд в WPF основана на классах RoutedCommand и RoutedEvent. Команда не обязательно должна принадлежать классу RoutedCommand. Можно просто реализовать интерфейс ICommand (будет рассмотрен позднее). Большая часть этого раздела посвящена описанию модели RoutedCommand.

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

Примерами команд являются операции редактирования Копировать, Вырезать и Вставить, доступные во многих приложениях. Семантика команды унифицирована для различных приложений и классов, однако логика действия специфична для конкретного объекта. Сочетание клавиш CTRL+X вызывает команду Вырезать в классах текста, классах изображения и веб-обозревателях. Однако фактическая логика выполнения операции Вырезать определяется объектом или приложением, в котором она выполняется, а не источником, в котором она вызывается. В текстовом объекте можно вырезать и поместить выделенный текст в буфер обмена. В графическом объекте можно вырезать выделенное изображение. Однако для вызова команды в обоих классах может использоваться один и тот же источник команды, например объект KeyGesture или кнопка на объекте ToolBar.

Простой пример команды в WPF

К самым простым способам использования команды в WPF можно отнести применение предопределенного класса RoutedCommand одного из классов библиотеки команд; использование элемента управления с собственной поддержкой обработки команд; а также использование элемента управления с собственной поддержкой вызова команд. Команда Paste представляет собой одну из предопределенных команд класса ApplicationCommands. Элемент управления TextBox содержит встроенную логику обработки команды Paste. В классе MenuItem реализована встроенная поддержка вызова команд.

В следующем примере описывается порядок настройки объекта MenuItem таким образом, что при его выборе вызывается команда Paste для объекта TextBox (если в объекте TextBox установлен фокус клавиатуры).

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste" />
  </Menu>
  <TextBox />
</StackPanel>
// Creating the UI objects
StackPanel mainStackPanel = new StackPanel();
TextBox pasteTextBox = new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();

// Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);

// Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste;

Четыре основных понятия в системе команд WPF

В модели маршрутизации команд в WPF можно выделить четыре основных понятия: команда, источник команды, цель команды и привязка команды.

  • Команда — выполняемое действие.

  • Источник команды — объект, которым вызывается команда.

  • Цель команды — объект, для которого выполняется команда.

  • Привязка команды — объект, используемый для сопоставления логики команды самой команде.

В предыдущем примере Paste — это команда, MenuItem — источник команды, TextBox — цель команды. Привязка команды предоставляется элементом управления TextBox. Необходимо отметить, что привязка CommandBinding не всегда предоставляется элементом управления, который принадлежит классу цели команды. Довольно часто объект CommandBinding создается разработчиком приложения. Также объект CommandBinding может быть присоединен к предку цели команды.

Команды

Команды в WPF создаются путем реализации интерфейса ICommand. ICommand предоставляет два метода: Execute и CanExecute, а также событие CanExecuteChanged. Execute выполняется действия, связанные с командой. CanExecute определяет, можно ли выполнить команду для текущего целевого объекта команды. Событие CanExecuteChanged вызывается, если диспетчер команд, в котором централизованно выполняются командные операции, обнаруживает в источнике команды изменения, которые могут сделать недействительной команду, вызванную, но еще не выполненную привязкой команды. В WPF реализация интерфейса ICommand принадлежит классу RoutedCommand и является основной темой данного раздела.

Основными источниками входных данных в WPF являются мышь, клавиатура, рукописный ввод и перенаправленные команды. При аппаратно-ориентированном вводе уведомление объектов на странице приложения о возникновении события ввода осуществляется с помощью объекта RoutedEvent. То же относится и к объектам RoutedCommand. Методы Execute и CanExecute класса RoutedCommand не содержат логику приложения для команды. Вместо этого они используются для вызова перенаправленных событий, которые проходят и поднимаются по дереву до тех пор, пока не будет найден объект с привязкой CommandBinding. В классе CommandBinding представлены обработчики для этих событий, а также обработчики, предназначенные для выполнения команды. Дополнительные сведения о маршрутизации событий в WPF см. в разделе Общие сведения о перенаправленных событиях.

Метод Execute объекта RoutedCommand используется для вызова событий PreviewExecuted и Executed для цели команды. Метод CanExecute объекта RoutedCommand используется для вызова событий CanExecute и PreviewCanExecute для цели команды. Эти события проходят и поднимаются по дереву элементов до тех пор, пока не будет найден объект с привязкой CommandBinding для этой конкретной команды.

В следующих классах WPF представлен набор общих перенаправленных команд: MediaCommands, ApplicationCommands, NavigationCommands, ComponentCommands и EditingCommands. Эти классы состоят только из объектов RoutedCommand и не включают в себя реализацию логики команды. Реализация логики осуществляется в объекте, для которого выполняется команда.

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

Источником команды является объект, который вызывает команду. Примерами источников команды являются объекты MenuItem, Button и KeyGesture.

Источники команд в WPF обычно реализуют интерфейс ICommandSource.

В интерфейсе ICommandSource предоставляются три свойства: Command, CommandTarget и CommandParameter:

  • Command — команда, которая выполняется при вызове источника команды.

  • CommandTarget — объект, для которого выполняется команда. Необходимо отметить, что в WPF свойство CommandTarget интерфейса ICommandSource применимо только в том случае, если интерфейс ICommand принадлежит классу RoutedCommand. Если для свойства CommandTarget установлено значение ICommandSource, а соответствующая команда не принадлежит классу RoutedCommand, цель команды игнорируется. Если значение свойства CommandTarget не задано, в качестве цели команды используется элемент, в котором установлен фокус клавиатуры.

  • CommandParameter — тип данных, определяемый пользователем, который используется для передачи информации обработчикам, реализующим команду.

В WPF представлены следующие классы, реализующие интерфейс ICommandSource: ButtonBase, MenuItem, Hyperlink и InputBinding. При щелчке объектов ButtonBase, MenuItem и Hyperlink вызывается соответствующая команда. Объект InputBinding вызывает команду при выполнении связанного с ним объекта InputGesture.

В следующем примере описывается порядок использования объекта MenuItem в ContextMenu в качестве источника для команды Properties.

<StackPanel>
  <StackPanel.ContextMenu>
    <ContextMenu>
      <MenuItem Command="ApplicationCommands.Properties" />
    </ContextMenu>
  </StackPanel.ContextMenu>
</StackPanel>
StackPanel cmdSourcePanel = new StackPanel();
ContextMenu cmdSourceContextMenu = new ContextMenu();
MenuItem cmdSourceMenuItem = new MenuItem();

// Add ContextMenu to the StackPanel.
cmdSourcePanel.ContextMenu = cmdSourceContextMenu;
cmdSourcePanel.ContextMenu.Items.Add(cmdSourceMenuItem);

// Associate Command with MenuItem.
cmdSourceMenuItem.Command = ApplicationCommands.Properties;

Как правило, источник команды будет осуществлять прослушивание события CanExecuteChanged. Это событие служит для оповещения источника команды о том, что изменена возможность выполнения команды для текущей цели команды. Источник команды может запрашивать текущее состояние объекта RoutedCommand с помощью метода CanExecute. Затем источник команды может отключить сам себя, если команда не может быть выполнена. Примером этого является выделение объекта MenuItem серым цветом, если команда не может быть выполнена.

Объект InputGesture может использоваться в качестве источника команды. В WPF существует два типа входных жестов: KeyGesture и MouseGesture. Объект KeyGesture можно сравнить с сочетанием клавиш, например CTRL+C. Объект KeyGesture включает в себя объект Key и набор объектов ModifierKeys. Объект MouseGesture включает в себя объект MouseAction и набор необязательных объектов ModifierKeys.

Для выполнения объекта InputGesture в качестве источника команды следует связать его с командой. Для этого предусмотрено несколько способов. Одним из способов является использование объекта InputBinding.

В следующем примере описывается порядок создания привязки KeyBinding между объектами KeyGesture и RoutedCommand.

<Window.InputBindings>
  <KeyBinding Key="B"
              Modifiers="Control" 
              Command="ApplicationCommands.Open" />
</Window.InputBindings>
KeyGesture OpenKeyGesture = new KeyGesture(
    Key.B,
    ModifierKeys.Control);

KeyBinding OpenCmdKeybinding = new KeyBinding(
    ApplicationCommands.Open,
    OpenKeyGesture);

this.InputBindings.Add(OpenCmdKeybinding);

Другим способом связывания объектов InputGesture и RoutedCommand является добавление объекта InputGesture в коллекцию InputGestureCollection объекта RoutedCommand.

В следующем примере описывается порядок добавления объекта KeyGesture в коллекцию InputGestureCollection объекта RoutedCommand.

KeyGesture OpenCmdKeyGesture = new KeyGesture(
    Key.B,
    ModifierKeys.Control);

ApplicationCommands.Open.InputGestures.Add(OpenCmdKeyGesture);

Класс CommandBinding

Объект CommandBinding предназначен для связывания команды с обработчиками событий, которые реализуют ее.

Класс CommandBinding содержит свойство Command, а также события PreviewExecuted, Executed PreviewCanExecute и CanExecute.

Свойство Command представляет собой команду, с которой связывается объект CommandBinding. Логика команды реализуется с помощью обработчиков событий, которые присоединены к событиям PreviewExecuted и Executed. Обработчики событий, присоединенные к событиям PreviewCanExecute и CanExecute, определяют возможность выполнения команды для текущей цели команды.

В следующем примере описывается порядок создания привязки CommandBinding в корневом объекте Window приложения. Объект CommandBinding связывает команду Open с обработчиками событий Executed и CanExecute.

<Window.CommandBindings>
  <CommandBinding Command="ApplicationCommands.Open"
                  Executed="OpenCmdExecuted"
                  CanExecute="OpenCmdCanExecute"/>
</Window.CommandBindings>
// Creating CommandBinding and attaching an Executed and CanExecute handler
CommandBinding OpenCmdBinding = new CommandBinding(
    ApplicationCommands.Open,
    OpenCmdExecuted,
    OpenCmdCanExecute);

this.CommandBindings.Add(OpenCmdBinding);

Далее создаются обработчики событий ExecutedRoutedEventHandler и CanExecuteRoutedEventHandler. Обработчик ExecutedRoutedEventHandler открывает объект MessageBox, в котором отображаются сведения о выполнении команды. Обработчик CanExecuteRoutedEventHandler устанавливает значение true свойства CanExecute.

Private Sub OpenCmdExecuted(ByVal sender As Object, ByVal e As ExecutedRoutedEventArgs)
    Dim command, targetobj As String
    command = CType(e.Command, RoutedCommand).Name
    targetobj = CType(sender, FrameworkElement).Name
    MessageBox.Show("The " + command + " command has been invoked on target object " + targetobj)
End Sub
void OpenCmdExecuted(object target, ExecutedRoutedEventArgs e)
{
    String command, targetobj;
    command = ((RoutedCommand)e.Command).Name;
    targetobj = ((FrameworkElement)target).Name;
    MessageBox.Show("The " + command +  " command has been invoked on target object " + targetobj);
}
Private Sub OpenCmdCanExecute(ByVal sender As Object, ByVal e As CanExecuteRoutedEventArgs)
    e.CanExecute = True
End Sub
void OpenCmdCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
    e.CanExecute = true;
}

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

В некоторых случаях к цели команды присоединяется сам объект CommandBinding, как например в случае с классом TextBox и командами Cut, Copy и Paste. Однако во многих случаях рекомендуется присоединять объект CommandBinding к предку цели команды, например главному объекту Window или объекту Application, особенно в тех случаях, когда один и тот же объект CommandBinding может использоваться для нескольких целей команды. Это решение принимается на стадии разработки после создания инфраструктуры команд.

Цель команды

Целью команды является элемент, для которого она выполняется. В отношении RoutedCommand целью команды является элемент, с которого начинается маршрутизация событий Executed и CanExecute. Как было отмечено ранее, в WPF свойство CommandTarget интерфейса ICommandSource применяется только в тех случаях, когда интерфейс ICommand принадлежит классу RoutedCommand. Если для свойства CommandTarget установлено значение ICommandSource, а соответствующая команда не принадлежит классу RoutedCommand, цель команды игнорируется.

Цель команды может быть явно задана в источнике команды. Если цель команды не определена, в этом качестве используется элемент, в котором установлен фокус клавиатуры. Одним из преимуществ использования в качестве цели команды элемента, в котором установлен фокус клавиатуры, является возможность использования одного источника команды для вызова команд для нескольких целей (в этом случае не требуется отслеживать цель команды). Например, если объект MenuItem вызывает команду Вставить в приложении, в котором содержатся элементы управления TextBox и PasswordBox, в качестве цели могут выступать объекты TextBox и PasswordBox (в зависимости от того, в каком из них установлен фокус клавиатуры).

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

<StackPanel>
  <Menu>
    <MenuItem Command="ApplicationCommands.Paste"
              CommandTarget="{Binding ElementName=mainTextBox}" />
  </Menu>
  <TextBox Name="mainTextBox"/>
</StackPanel>
// Creating the UI objects
StackPanel mainStackPanel = new StackPanel();
TextBox pasteTextBox = new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();

// Adding objects to the panel and the menu
stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);

// Setting the command to the Paste command
pasteMenuItem.Command = ApplicationCommands.Paste;

Класс CommandManager

В классе CommandManager представлен ряд связанных с командами функций. В нем представлен набор статических методов для добавления и удаления обработчиков событий PreviewExecuted, Executed, PreviewCanExecute и CanExecute для определенного элемента. Также в нем представлены средства для регистрации объектов CommandBinding и InputBinding в определенном классе. Также в классе CommandManager представлены средства, в которых используется событие RequerySuggested для уведомления команды о необходимости вызова события CanExecuteChanged.

Метод InvalidateRequerySuggested используется для принудительного вызова объектом CommandManager события RequerySuggested. Это используется в тех случаях, когда необходимо отключить или включить команду, если такие случаи не учитываются объектом CommandManager. Пример вызова метода InvalidateRequerySuggested для принудительного вызова диспетчером CommandManager события RequerySuggested см. в разделе Пример Disable Command Source Via Dispatcher Timer.

Библиотека команд

В WPF представлен набор предопределенных команд. Библиотека команд включает следующие классы: ApplicationCommands, NavigationCommands, MediaCommands, EditingCommands и ComponentCommands. В этих классах представлены такие команды, как Cut, BrowseBack и BrowseForward, Play, Stop и Pause.

Большинство из этих команд включает в себя набор используемых по умолчанию привязок ввода. Например, при обработке в приложении команды копирования автоматически используется привязка сочетания клавиш "CTRL+C". Также можно получить привязки для других устройств ввода, например жестов пера Планшетный ПК или голосовых команд.

При ссылке на команды в различных библиотеках команд с помощью XAML обычно можно опустить имя класса библиотеки классов, которое предоставляет статическое свойство команды. Обычно имена команд задаются однозначно в виде строк. Собственные типы используются для логической группировки команд, но не являются обязательными для устранения неоднозначности. Например, можно использовать сокращенную форму команды Command="Cut" вместо Command="ApplicationCommands.Cut". Этот удобный механизм встроен в обработчик команд WPF XAML (более точно, это поведение преобразователя типа ICommand), на который во время загрузки ссылается обработчик WPF XAML.

Создание пользовательских команд

При необходимости можно создать собственные команды, расширяющие возможности, представленные в классах библиотеки команд. Существует два способа создания пользовательской команды. Написание команды с нуля с реализацией интерфейса ICommand. Однако чаще применяется создание объекта RoutedCommand или RoutedUICommand.

Пример создания пользовательского объекта RoutedCommand см. в разделе Пример создания пользовательской команды RoutedCommand.

См. также

Задачи

Пример создания пользовательской команды RoutedCommand

Практическое руководство. Реализация ICommandSource

Как добавить команду в MenuItem

Пример Disable Command Source Via Dispatcher Timer

Основные понятия

Общие сведения о входных данных

Общие сведения о перенаправленных событиях

Ссылки

RoutedCommand

CommandBinding

InputBinding

CommandManager