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

  • Откройте файл 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 и составляет суть реализации логики доступности источников команд.


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: