- •Лабораторная работа: События и команды в 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. Разработка простого блокнота с использованием механизма команд
- •Создание нового проекта из копии существующего
- •Краткий анализ задачи
- •Создание и привязка команд
- •Реализация логики доступности источников команд
- •Отображение позиции курсора в строке состояния
Создание и привязка команд
Откройте файл Window1.xaml.cs текущего проекта Notepad2, найдите объявление поля modified и переименуйте его вновь в IsModified так
Было bool modified = false; // Флаг изменений содержимого
Стало bool IsModified = false; // Флаг изменений содержимого
Откройте файл EnabledControls.cs, уберите в нем лишний код, чтобы остался только такой
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Notepad2
{
partial class Window1
{
// Вызов размещен в конструкторе класса
void AdditionalHandlers()
{
}
}
}
Внесите в файл EnabledControls.cs код определения и инициализации команд вместе со встроенными жестами
namespace Notepad2
{
partial class Window1
{
// Объявляем и инициализируем поля команд
// Их обязательно нужно объявлять статическими, чтобы
// размещались в объекте-типе и уже были созданы перед
// созданием элементов, к которым присоединяются
// Последний параметр означает жесты
public static RoutedCommand SaveCommand = ApplicationCommands.Save;
public static RoutedCommand PageSetupCommand =
new RoutedCommand("PageSetup", typeof(Window1), null);// Без жеста
public static RoutedCommand UndoCommand = ApplicationCommands.Undo;
public static RoutedCommand RedoCommand = ApplicationCommands.Redo;
public static RoutedCommand CutCommand = ApplicationCommands.Cut;
public static RoutedCommand CopyCommand = ApplicationCommands.Copy;
public static RoutedCommand PasteCommand = ApplicationCommands.Paste;
public static RoutedCommand DeleteCommand = ApplicationCommands.Delete;
public static RoutedCommand FindNextCommand;// Определим в ст. конструкторе
public static RoutedCommand ReplaceCommand = ApplicationCommands.Replace;
public static RoutedCommand GoToCommand;// Определим в ст. конструкторе
public static RoutedCommand SelectAllCommand = ApplicationCommands.SelectAll;
// Вызов размещен в конструкторе класса
void AdditionalHandlers()
{
}
}
}
Мы объявили псевдонимы команд как общедоступные поля класса Window1. Команды, которые имеют встроенные жесты или не должны иметь жестов, мы инициализировали сразу. Две команды только объявили, но сами объекты собираемся создать в коде. Код создания этих команд мы поместим в статический конструктор для добавления жестов. Это нужно для того, что добавление жестов требует действий, а это разрешено только в методах. Конструктор должен быть обязательно статическим, чтобы мог выполниться до создания экземпляра окна. Все команды должны к этому времени уже существовать, поскольку используются в разметке окна при создании интерфейсных элементов.
Добавьте в класс Window1 файла EnabledControls.cs статический конструктор со следующим кодом
namespace Notepad2
{
partial class Window1
{
..............................................
// Статический конструктор
static Window1()
{
// Определяем с добавлением жестов
InputGestureCollection coll = new InputGestureCollection();
coll.Add(new KeyGesture(Key.F3, ModifierKeys.None, "F3"));
FindNextCommand = new RoutedCommand("FindNext", typeof(Window1), coll);
coll = new InputGestureCollection();
coll.Add(new KeyGesture(Key.G, ModifierKeys.Control, "Ctrl+G"));
GoToCommand = new RoutedCommand("GoTo", typeof(Window1), coll);
}
// Вызов размещен в конструкторе класса
void AdditionalHandlers()
{
}
}
}
Не забывайте, что статический конструктор класса в C# не принимает параметров и может существовать только в единственном экземпляре (если объявим).
Теперь в двух пользовательских командах имеются как жесты, так и вся необходимая информация для отображения в пунктах меню.
В соответствии с планом, присоединим команды к источникам, вначале в коде.
Добавьте в функцию AdditionalHandlers() файла EnabledControls.cs следующий код
void AdditionalHandlers()
{
Clipboard.Clear();// Временно, чтобы испытать начальное состояние
// Присоединяем команды к источникам, жесты уже встроены в команды
//SaveCommand - присоединим в разметке
//PageSetupCommand - присоединим в разметке
//UndoCommand - присоединим в разметке
//RedoCommand - присоединим в разметке
//CutCommand - присоединим в разметке
//CopyCommand - присоединим в разметке
btnPaste.Command = itemPaste.Command = contextPaste.Command = PasteCommand;
btnDelete.Command = itemDelete.Command = contextDelete.Command = DeleteCommand;
itemFindNext.Command = FindNextCommand;
itemReplace.Command = ReplaceCommand;
itemGoTo.Command = GoToCommand;
itemSelectAll.Command = SelectAllCommand;
}
Добавьте в открывающем дескрипторе <Window> файла Window1.xaml параметр отображения пространства имен класса процедурного кода на разметку, чтобы компилятор видел вставляемые в разметку команды. Имя отображения можно принять произвольно - выберите myCmd
Когда вы начнете вручную набирать запись xmlns:myCmd=, то после ввода знака присваивания IntelliSense выдаст подсказку, в которой нужно выбрать выделенную на снимке опцию списка
В результате будет догенерирована следующая запись
<Window x:Class="Notepad2.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myCmd="clr-namespace:Notepad2"
Title="Window1: Управление состоянием источников команд"
Width="500" Height="375"
MinWidth="500" MinHeight="375"
WindowStartupLocation="CenterScreen"
ResizeMode="CanResizeWithGrip"
Loaded="Window_Loaded"
Icon="Notepad.ico"
Closing="Window_Closing"
Activated="Window_Activated"
>
..............................................
</Window>
Теперь отредактируем разметку 12 запланированных источников задач в соответствии с таблицей
12 задач |
||
Где присоединить к источнику? |
Задача |
Alias (псевдоним) |
Разметка |
Save |
SaveCommand |
Разметка |
Page Setup |
PageSetupCommand |
Разметка |
Undo |
UndoCommand |
Разметка |
Redo |
RedoCommand |
Разметка |
Cut |
CutCommand |
Разметка |
Copy |
CopyCommand |
|
Paste |
PasteCommand |
|
Delete |
DeleteCommand |
|
Find Next |
FindNextCommand |
|
Replace |
ReplaceCommand |
|
Go To |
GoToCommand |
|
Select All |
SelectAllCommand |
В файле Window1.xaml текущего проекта удалите в источниках для первых 6 задач таблицы тексты жестов и замените записи с событием Click на записи с присоединением команд
В файле Window1.xaml текущего проекта удалите в источниках для последних 6 задач таблицы тексты жестов и записи события Click. Для этих источников присоединение команд мы уже выполнили в процедурном коде
Правленный код разметки станет таким (для удобства файл Window1.xaml с новым содержимым приводится полностью)
<Window x:Class="Notepad2.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myCmd="clr-namespace:Notepad2"
Title="Window1: Управление состоянием источников команд"
Width="500" Height="375"
MinWidth="500" MinHeight="375"
WindowStartupLocation="CenterScreen"
ResizeMode="CanResizeWithGrip"
Loaded="Window_Loaded"
Icon="Notepad.ico"
Closing="Window_Closing"
Activated="Window_Activated"
>
<Window.Resources>
<!-- File -->
<Image x:Shared="False" x:Key="iconNew" Source="Images/NewDocumentHS.png" Width="16" Height="16" />
<Image x:Shared="False" x:Key="iconOpen" Source="Images/OpenHS.png" Width="16" Height="16" />
<Image x:Shared="False" x:Key="iconSave" Source="Images/SaveHS.png" Width="16" Height="16" />
<Image x:Shared="False" x:Key="iconPageSetup" Source="Images/PrintSetupHS.png" Width="16" Height="16" />
<Image x:Shared="False" x:Key="iconPrintPreview" Source="Images/PrintPreviewHS.png" Width="16" Height="16" />
<Image x:Shared="False" x:Key="iconPrint" Source="Images/PrintHS.png" Width="16" Height="16" />
<!-- Edit -->
<Image x:Shared="False" x:Key="iconUndo" Source="Images/Edit_UndoHS.png" Width="16" Height="16" />
<Image x:Shared="False" x:Key="iconRedo" Source="Images/Edit_RedoHS.png" Width="16" Height="16" />
<Image x:Shared="False" x:Key="iconCut" Source="Images/CutHS.png" Width="16" Height="16" />
<Image x:Shared="False" x:Key="iconCopy" Source="Images/CopyHS.png" Width="16" Height="16" />
<Image x:Shared="False" x:Key="iconPaste" Source="Images/PasteHS.png" Width="16" Height="16" />
<Image x:Shared="False" x:Key="iconDelete" Source="Images/DeleteHS.png" Width="16" Height="16" />
<Image x:Shared="False" x:Key="iconFind" Source="Images/FindHS.png" Width="16" Height="16" />
<Image x:Shared="False" x:Key="iconFont" Source="Images/FontHS.png" Width="16" Height="16" />
</Window.Resources>
<DockPanel LastChildFill="True">
<!-- Меню -->
<Menu DockPanel.Dock="Top">
<MenuItem Header="_File">
<!-- Сокращенные варианты подключения иконок с использованием статических ресурсов -->
<MenuItem Name="itemNew" Click="NewOnExecute" Header="_New" InputGestureText="Ctrl+N"
Icon="{StaticResource iconNew}" />
<MenuItem Name="itemOpen" Click="OpenOnExecute" Header="_Open..." InputGestureText="Ctrl+O"
Icon="{StaticResource iconOpen}" />
<MenuItem Name="itemSave" Command="myCmd:Window1.SaveCommand" Header="_Save"
Icon="{StaticResource iconSave}" />
<MenuItem Name="itemSaveAs" Click="SaveAsOnExecute" Header="Save _As..." />
<Separator />
<MenuItem Name="itemPageSetup" Command="myCmd:Window1.PageSetupCommand" Header="Page Set_up..."
Icon="{StaticResource iconPageSetup}" />
<MenuItem Name="itemPrintPreview" Click="PrintPreviewOnExecute" Header="P_rint Preview"
InputGestureText="Ctrl+F2" Icon="{StaticResource iconPrintPreview}" />
<MenuItem Name="itemPrint" Click="PrintOnExecute" Header="_Print..."
InputGestureText="Ctrl+P" Icon="{StaticResource iconPrint}" />
<Separator />
<MenuItem Name="itemExit" Click="ExitOnExecute" Header="E_xit" />
</MenuItem>
<MenuItem Header="_Edit">
<MenuItem Name="itemUndo" Command="myCmd:Window1.UndoCommand" Header="_Undo" Icon="{StaticResource iconUndo}" />
<MenuItem Name="itemRedo" Command="myCmd:Window1.RedoCommand" Header="_Redo" Icon="{StaticResource iconRedo}" />
<Separator></Separator>
<MenuItem Name="itemCut" Command="myCmd:Window1.CutCommand" Header="Cu_t" Icon="{StaticResource iconCut}" />
<MenuItem Name="itemCopy" Command="myCmd:Window1.CopyCommand" Header="_Copy" Icon="{StaticResource iconCopy}" />
<MenuItem Name="itemPaste" Header="_Paste" Icon="{StaticResource iconPaste}" />
<MenuItem Name="itemDelete" Header="De_lete" Icon="{StaticResource iconDelete}" />
<Separator></Separator>
<MenuItem Name="itemFind" Click="FindOnExecute" Header="_Find..." InputGestureText="Ctrl+F"
Icon="{StaticResource iconFind}" />
<MenuItem Name="itemFindNext" Header="Find _Next" />
<MenuItem Name="itemReplace" Header="_Replace..." />
<MenuItem Name="itemGoTo" Header="_Go To..." />
<Separator></Separator>
<MenuItem Name="itemSelectAll" Header="Select _All" />
</MenuItem>
<MenuItem Header="F_ormat">
<MenuItem Name="itemFont" Click="FontOnExecute" Header="_Font..." Icon="{StaticResource iconFont}" />
<Separator />
<MenuItem Name="itemWordWrap" Click="WordWrapOnExecute" Header="_Word Wrap" IsCheckable="True"
IsChecked="True" InputGestureText="Ctrl+W" />
</MenuItem>
<MenuItem Header="_Help">
<MenuItem Name="itemAbout" Click="AboutOnExecute" Header="_About" />
</MenuItem>
</Menu>
<!-- Панель инструментов -->
<ToolBarTray DockPanel.Dock="Top">
<ToolBar>
<Button Name="btnNew" Click="NewOnExecute" Width="23" Content="{StaticResource iconNew}" />
<Button Name="btnOpen" Click="OpenOnExecute" Width="23" Content="{StaticResource iconOpen}" />
<Button Name="btnSave" Command="myCmd:Window1.SaveCommand" Width="23" Content="{StaticResource iconSave}" />
</ToolBar>
<ToolBar>
<Button Name="btnUndo" Command="myCmd:Window1.UndoCommand" Width="23" Content="{StaticResource iconUndo}" />
<Button Name="btnRedo" Command="myCmd:Window1.RedoCommand" Width="23" Content="{StaticResource iconRedo}" />
<Separator />
<Button Name="btnCut" Command="myCmd:Window1.CutCommand" Width="23" Content="{StaticResource iconCut}" />
<Button Name="btnCopy" Command="myCmd:Window1.CopyCommand" Width="23" Content="{StaticResource iconCopy}" />
<Button Name="btnPaste" Width="23" Content="{StaticResource iconPaste}" />
<Button Name="btnDelete" Width="23" Content="{StaticResource iconDelete}" />
</ToolBar>
<ToolBar Header="Find:">
<TextBox Width="100" />
<Button Name="btnFind" Click="FindOnExecute" Width="23" Content="{StaticResource iconFind}" />
</ToolBar>
</ToolBarTray>
<!-- Строка состояния -->
<StatusBar DockPanel.Dock="Bottom" Height="32" Name="statusBar">
<Label>Simulator Application is Loading</Label>
<Separator />
<ProgressBar Height="20" Width="100" IsIndeterminate="True" />
</StatusBar>
<!-- Многострочное текстовое поле редактирования -->
<TextBox TextWrapping="Wrap"
AcceptsReturn="True"
AcceptsTab="True"
VerticalScrollBarVisibility="Auto"
Name="txtBox1"
TextChanged="txtBox1_TextChanged"
HorizontalScrollBarVisibility="Auto"
>
<TextBox.ContextMenu>
<ContextMenu Width="100">
<MenuItem Name="contextCut" Command="myCmd:Window1.CutCommand" Header="Cu_t" Icon="{StaticResource iconCut}" />
<MenuItem Name="contextCopy" Command="myCmd:Window1.CopyCommand" Header="_Copy" Icon="{StaticResource iconCopy}"/>
<MenuItem Name="contextPaste" Header="_Paste" Icon="{StaticResource iconPaste}" />
<MenuItem Name="contextDelete" Header="De_lete" Icon="{StaticResource iconDelete}" />
</ContextMenu>
</TextBox.ContextMenu>
</TextBox>
</DockPanel>
</Window>
Эта разметка визуально получилась достаточно широкой, ее лучше прежде скопировать, а потом разбирать.
Удалите (я закомментировал) код создания жестов в функции CreateGestures() файла KeyGestures.cs для выбранных нами 12 задач (для задачи Page Setup жестов нет)
void CreateGestures()
{
// File
gests.Add(new KeyGesture(Key.N, ModifierKeys.Control), NewOnExecute);//_New
gests.Add(new KeyGesture(Key.O, ModifierKeys.Control), OpenOnExecute);//_Open...
//gests.Add(new KeyGesture(Key.S, ModifierKeys.Control), SaveOnExecute);//_Save
gests.Add(new KeyGesture(Key.F2, ModifierKeys.Control), PrintPreviewOnExecute);//P_rint Preview
gests.Add(new KeyGesture(Key.P, ModifierKeys.Control), PrintOnExecute);//_Print...
// Edit
//gests.Add(new KeyGesture(Key.Z, ModifierKeys.Control), UndoOnExecute);//_Undo
//gests.Add(new KeyGesture(Key.Y, ModifierKeys.Control), RedoOnExecute);//_Redo
//gests.Add(new KeyGesture(Key.X, ModifierKeys.Control), CutOnExecute);//Cu_t
//gests.Add(new KeyGesture(Key.C, ModifierKeys.Control), CopyOnExecute);//_Copy
//gests.Add(new KeyGesture(Key.V, ModifierKeys.Control), PasteOnExecute);//_Paste
//gests.Add(new KeyGesture(Key.Delete, ModifierKeys.None), DeleteOnExecute);//De_lete
gests.Add(new KeyGesture(Key.F, ModifierKeys.Control), FindOnExecute);//_Find...
//gests.Add(new KeyGesture(Key.F3, ModifierKeys.None), FindNextOnExecute);//Find _Next
//gests.Add(new KeyGesture(Key.H, ModifierKeys.Control), ReplaceOnExecute);//_Replace...
//gests.Add(new KeyGesture(Key.G, ModifierKeys.Control), GoToOnExecute);//_Go To...
//gests.Add(new KeyGesture(Key.A, ModifierKeys.Control), SelectAllOnExecute);//Select _All
// Format
gests.Add(new KeyGesture(Key.W, ModifierKeys.Control), WordWrapOnExecute);//_Word Wrap
}
Запустите приложение и убедитесь, что тексты жестов во всех источниках команд присутствуют несмотря на то, что мы их только что явно удалили. Теперь жесты в источники попадают из команд
В начальном состоянии источники команд являются недоступными, поскольку пока нет привязки к элементам визуального дерева и обработчикам. Исправим это и привяжем команды к коллекции CommandBindings окна Window1. Причем, в соответствии с намеченным планом выполним привязку одной части команд в коде, а остальной - в разметке.
В месте привязки для каждого объекта привязки нужно указать имя команды, имя обработчика события Executed и имя обработчика события CanExecute для управления доступностью источников. Оба события всплывающие, поэтому местом привязки можно выбрать любой элемент маршрута, например, окно Window1. Это корень визуального дерева и ни одно всплывающее событие мимо него не пройдет.
Обработчики события Executed для выполнения команд у нас есть, мы их уже создавали для обработки события Click. Например, для команды Save обработчик имеет имя SaveOnExecute и его сигнатура выглядит так
private void SaveOnExecute(object sender, RoutedEventArgs e)
{
}
Нам предстоит создать обработчики события CanExecute и мы разместим их в файле EnabledControls.cs. Они будут иметь несколько иную сигнатуру. Например, для команды Save такой обработчик должен иметь следующую заготовку
private void SaveCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
}
Добавьте в класс Window1 файла EnabledControls.cs заготовки обработчиков события CanExecute для выбранных нами ранее 12 команд (число-то какое хорошее!)
// Обработчики события CanExecute команд
private void SaveCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
}
private void PageSetupCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
}
private void UndoCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
}
private void RedoCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
}
private void CutCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
}
private void CopyCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
}
private void PasteCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
}
private void DeleteCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
}
private void FindNextCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
}
private void ReplaceCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
}
private void GoToCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
}
private void SelectAllCanExecute(object sender, CanExecuteRoutedEventArgs e)
{
}
Теперь пришла пора выполнить саму привязку в соответствии с намеченным планом
План 12 задач, которые предстоит реализовать командами |
||||
Где привязать к окну? |
Задача |
Alias (псевдоним) |
Жесты |
Регулировать доступность источников? |
Разметка |
Save |
SaveCommand |
Ctrl+S |
Да |
Разметка |
Page Setup |
PageSetupCommand |
|
Нет |
Разметка |
Undo |
UndoCommand |
Ctrl+Z |
Да |
Код |
Redo |
RedoCommand |
Ctrl+Y |
Да |
Код |
Cut |
CutCommand |
Ctrl+X |
Да |
Код |
Copy |
CopyCommand |
Ctrl+C |
Да |
Разметка |
Paste |
PasteCommand |
Ctrl+V |
Да |
Разметка |
Delete |
DeleteCommand |
Del |
Да |
Разметка |
Find Next |
FindNextCommand |
F3 |
Да |
Код |
Replace |
ReplaceCommand |
Ctrl+H |
Да |
Код |
Go To |
GoToCommand |
Ctrl+G |
Нет |
Код |
Select All |
SelectAllCommand |
Ctrl+A |
Да |
Обратите внимание, что источники команд PageSetupCommand и GoToCommand должны быть доступны всегда, поэтому привяжем для них только обработчики события Executed
Удалите в файле EnabledControls.cs заготовки обработчиков события CanExecute для команд PageSetupCommand и GoToCommand (или не удаляйте и пусть болтаются как незадействованные методы)
В файле Window1.xaml текущего проекта выполните привязку части команд к объекту окна, для этого после открывающего дескриптора окна вставьте следующую разметку
<Window x:Class="Notepad2.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:myCmd="clr-namespace:Notepad2"
Title="Window1: Управление состоянием источников команд"
Width="500" Height="375"
MinWidth="500" MinHeight="375"
WindowStartupLocation="CenterScreen"
ResizeMode="CanResizeWithGrip"
Loaded="Window_Loaded"
Icon="Notepad.ico"
Closing="Window_Closing"
Activated="Window_Activated"
>
<!-- Привязка команд к объекту окна -->
<Window.CommandBindings>
<CommandBinding Command="myCmd:Window1.SaveCommand" Executed="SaveOnExecute" CanExecute="SaveCanExecute" />
<CommandBinding Command="myCmd:Window1.PageSetupCommand" Executed="PageSetupOnExecute" />
<CommandBinding Command="myCmd:Window1.UndoCommand" Executed="UndoOnExecute" CanExecute="UndoCanExecute" />
<CommandBinding Command="myCmd:Window1.PasteCommand" Executed="PasteOnExecute" CanExecute="PasteCanExecute" />
<CommandBinding Command="myCmd:Window1.DeleteCommand" Executed="DeleteOnExecute" CanExecute="DeleteCanExecute" />
<CommandBinding Command="myCmd:Window1.FindNextCommand" Executed="FindNextOnExecute" CanExecute="FindNextCanExecute" />
</Window.CommandBindings>
..........................................
</Window>
В файле EnabledControls.cs добавьте в функцию AdditionalHandlers() код привязки части команд к объекту окна, после чего функция должна стать такой
// Вызов размещен в конструкторе класса
void AdditionalHandlers()
{
Clipboard.Clear();// Временно, чтобы испытать начальное состояние
// Присоединяем команды к источникам, жесты уже встроены в команды
//SaveCommand - присоединим в разметке
//PageSetupCommand - присоединим в разметке
//UndoCommand - присоединим в разметке
//RedoCommand - присоединим в разметке
//CutCommand - присоединим в разметке
//CopyCommand - присоединим в разметке
btnPaste.Command = itemPaste.Command = contextPaste.Command = PasteCommand;
btnDelete.Command = itemDelete.Command = contextDelete.Command = DeleteCommand;
itemFindNext.Command = FindNextCommand;
itemReplace.Command = ReplaceCommand;
itemGoTo.Command = GoToCommand;
itemSelectAll.Command = SelectAllCommand;
// Привязка части команд к объекту окна в коде
// 0)
this.CommandBindings.Add(new CommandBinding(RedoCommand, RedoOnExecute, RedoCanExecute));
this.CommandBindings.Add(new CommandBinding(CutCommand, CutOnExecute, CutCanExecute));
this.CommandBindings.Add(new CommandBinding(CopyCommand, CopyOnExecute, CopyCanExecute));
// Теперь чуть подлиннее: создаем, настраиваем, привязываем!
// 1)
CommandBinding binding = new CommandBinding();
binding.Command = ReplaceCommand;
binding.Executed += ReplaceOnExecute;
binding.CanExecute += ReplaceCanExecute;
this.CommandBindings.Add(binding);
// 2)
binding = new CommandBinding(GoToCommand);
binding.Executed += GoToOnExecute;
this.CommandBindings.Add(binding);
// 3)
binding = new CommandBinding(SelectAllCommand, SelectAllOnExecute);
binding.CanExecute += SelectAllCanExecute;
this.CommandBindings.Add(binding);
}
После того, как ссылка binding, которая пока единствественная адресовала объект, передаст адрес закрепленного за ней объекта в коллекцию CommandBindings элемента, этот объект станет адресоваться в двух местах: в коллекции и в ссылке. Поэтому такая ссылка станет уже излишней и ее можно будет использовать для присвоения адреса нового объекта. А коллекция так и будет продолжать адресовать брошенный ссылкой объект. В коде показано применение вариантов перегрузок конструктора класса CommandBinding и настройки свойств объекта.
Запустите приложение и убедитесь в следующем...
Источники, к которым мы присоединили команды, по прежнему остаются недоступными, как после того, как мы к ним команды только присоединили. Но заметьте, что два источника: File/Page Setup... и Edit/Go To... открылись и функционируют, а для Go To даже клавиатурный жест Ctrl+G действует нормально. Это происходит потому, что мы для них не зарегистрировали обработчики событий CanExecute и они свободно вызывают обработчики, зарегистрированные в событии Executed, а остальные источники ждут разрешение на доступность. Создание таких разрешений в обработчиках CanExecute и составляет суть реализации логики доступности источников команд.
