- •Лабораторная работа: События и команды в wpf
- •Обзор библиотечных событий
- •Упражнение 1. Обработка событий клавиатуры
- •События мыши
- •Упражнение 2. Прослушивание событий мыши
- •Упражнение 3. Создание и прослушивание пользовательского события
- •Обработчики уровня класса
- •Добавление информации в объект аргумента события
- •Задание для Упражнения 3
- •Модель команд
- •Объекты команд
- •Библиотечные классы команд
- •Присоединение команды к источнику
- •Привязка команды к прослушивающему элементу
- •Упражнение 4. Привязка команд в разметке
- •Перекрытие функций диспетчеризации событий
- •Прямой вызов команд
- •Упражнение 5. Привязка команд в процедурном коде
- •Жесты как источники команд
- •Добавление жестов в команду
- •Способ 1
- •Способ 2
- •Способ 3
- •Добавление жестов в прослушивающий элемент
- •Упражнение 6. Разработка простого блокнота без механизма команд
- •Создание главного меню
- •Добавление иконок
- •Создание логических ресурсов
- •Создание панели инструментов, строки состояния и рабочей области
- •Замена встроенного контекстного меню
- •Назначение ресурсов неразделяемыми
- •Замена иконок на прозрачные
- •Отключение встроенных команд
- •Подключение иконки приложения
- •Распределение класса по нескольким файлам и создание вспомогательных функций
- •Размещение вариантов заголовков окна в ресурсах приложения
- •Создание заготовок обработчиков
- •Регистрация обработчиков в разметке
- •Реализация обработчиков раздела меню File
- •Обработка системной кнопки
- •Реализация части обработчиков раздела меню Edit
- •Разработка и кодирование диалогового окна Find and Replace
- •Кодирование функциональности Find and Replace в основном окне
- •Подключение функциональности Find and Replace к источникам задач
- •Разработка диалогового окна Go To
- •Применение вложенных ресурсов
- •Подключение функциональности Go To к основному окну
- •Прочие задачи
- •Принудительная перерисовка окна
- •Добавление жестов
- •Логика отключения источников задач
- •Реализация логики отключения источников задачи Save
- •Упражнение 7. Разработка простого блокнота с использованием механизма команд
- •Создание нового проекта из копии существующего
- •Краткий анализ задачи
- •Создание и привязка команд
- •Реализация логики доступности источников команд
- •Отображение позиции курсора в строке состояния
Объекты команд
Объекты команд порождаются классом RoutedCommand, который единственный в WPF реализует (и наследует, конечно) интерфейс ICommand, объявленный следующим образом
public interface ICommand
{
event EventHandler CanExecuteChanged;
bool CanExecute(object parameter);
void Execute(object parameter);
}
Реализующий класс RoutedCommand расширяет сигнатуру объявленных в ICommand методов до второго аргумента target
public bool CanExecute(object parameter, System.Windows.IInputElement target)
public void Execute(object parameter, System.Windows.IInputElement target)
где - аргумент parameter содержит сведения о команде, а аргумент target - сведения о целевом элементе команды.
При возбуждении элемента-источника, к которому прикреплена команда (объект RoutedCommand ), вызывается метод CanExecute(). Этот метод возбуждает туннельное событие PreviewCanExecute, которое начинает спускаться от корня визуального дерева элементов к источнику команды и собирает информацию с целевых элементов о возможности выполнения команды. Получив результаты опроса от события PreviewCanExecute метод CanExecute() возбуждает парное восходящее событие CanExecute с заданием оповестить целевые элементы принять состояние в зависимости от полученной информации от события PreviewCanExecute, а сам возвращает флаг возможности или невозможности выполнения команды.
Если метод CanExecute() вернул значение true, значит исполнение команды возможно и следом вызывается метод Execute(), который последовательно возбуждает парные события PreviewExecuted и Executed как указание выполнить команду во всех целевых элементах прикрепленными к ним обработчиками. Событие отмены выполнения CanExecuteChanged вызывается, если диспетчер команд, в котором централизованно выполняются командные операции, обнаруживает в источнике команды изменения, которые могут сделать недействительной команду, вызванную, но еще не выполненную в целевом элементе.
Библиотечные классы команд
Библиотека .NET Framework имеет множество готовых команд, наиболее часто встречающихся в приложениях. Команды представлены в пространствах имен System.Windows. Input и System.Windows. Documents следующими статическими классами:
ApplicationCommands
ComponentCommands
MediaCommands
NavigationCommands
EditingCommands
В каждом из приведенных классов команда объявлена как статическое свойство только для чтения типа RoutedUICommand (или RoutedCommand ), следовательно при использовании команды создавать экземпляр класса команды не нужно. Команды имеют имена, ассоциируемые с выполняемой задачей, например, Cut, Copy, New и т.д.
Присоединение команды к источнику
Чтобы команда работала, ее нужно присоединить к свойству Command источника команды. Источников, к которым присоединена одна и та же команда, может быть несколько. Элементы, к которым можно присоединить команду, должны наследовать интерфейс ICommandSource. Только такие элементы будут иметь свойство Command. Этих элементов в WPF немного и все они показаны на рисунке
Интерфейс ICommandSource реализует свои свойства в ближайших наследниках ButtonBase, Hyperlink, InputBinding, MenuItem по общему синтаксису:
public System.Windows.Input.ICommand Command { get; set; }
public object CommandParameter { get; set; }
public System.Windows.IInputElement CommandTarget { get; set; }
В источнике для присоединения команды достаточно присвоить значение только ссылке Command на объект команды (на экземпляр RoutedCommand или RoutedUICommand ). Остальные свойства являются необязательными и служат для уточнения команды:
CommandParameter - тип данных, определяемый пользователем, который используется для передачи информации обработчикам, реализующим команду
CommandTarget - идентифицирует элемент, в котором должна выполняться данная команда. Если значение свойства CommandTarget не задано, в качестве цели команды используется элемент, в котором установлен фокус ввода
Обратите внимание, что два наследуемые источниками команд свойства-ссылки Command и CommandTarget имеют типы интерфейсов, что позволяет адресовать любые команды и целевые элементы, наследующие эти интерфейсы.
Вот, например, как можно присоединить команду Paste к элементу меню в разметке
<StackPanel>
<Menu Header="Edit">
<MenuItem Command="ApplicationCommands.Paste" />
</Menu>
<TextBox />
</StackPanel>
Для библиотечных команд синтаксис разметки допускает использование сокращенной записи, например
<StackPanel>
<Menu Header="Edit">
<MenuItem Command="Paste" />
</Menu>
<TextBox />
</StackPanel>
Теперь, если объект TextBox имеет фокус ввода и буфер обмена содержит текст, то при выборе пользователем этого пункта меню текст из буфера будет вставлен в элемент TextBox. Обратите внимание, что объект MenuItem не устанавливает свойство Header для отображения в нем названия команды Paste. Если свойство для текста названия команды в источнике опущено, то источник способен сам извлекать эту информацию из объекта присоединенной команды.
А вот альтернативный вариант присоединения команды Paste к элементу меню в процедурном коде
// Создание объектов пользовательского интерфейса
StackPanel mainStackPanel = new StackPanel();
TextBox pasteTextBox = new TextBox();
Menu stackPanelMenu = new Menu();
MenuItem pasteMenuItem = new MenuItem();
// Добавление объектов в коллекции панели и меню
stackPanelMenu.Items.Add(pasteMenuItem);
mainStackPanel.Children.Add(stackPanelMenu);
mainStackPanel.Children.Add(pasteTextBox);
// Присоединение библиотечной команды Paste к источнику команд
pasteMenuItem.Command = ApplicationCommands.Paste;
Аналогичный пример можно привести для присоединения команды к контекстному меню.
<StackPanel>
<StackPanel.ContextMenu>
<ContextMenu>
<MenuItem Command="ApplicationCommands.Paste" />
</ContextMenu>
</StackPanel.ContextMenu>
</StackPanel>
// Создание объектов пользовательского интерфейса
StackPanel cmdSourcePanel = new StackPanel();
ContextMenu cmdSourceContextMenu = new ContextMenu();
MenuItem cmdSourceMenuItem = new MenuItem();
// Добавление ContextMenu в StackPanel
cmdSourcePanel.ContextMenu = cmdSourceContextMenu;
cmdSourcePanel.ContextMenu.Items.Add(cmdSourceMenuItem);
// Присоединение библиотечной команды Paste к источнику команд
cmdSourceMenuItem.Command = ApplicationCommands.Paste;
