Реализация логики отключения источников задачи Save

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

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

  • Выделите узел текущего проекта и командой Project/Add Class добавьте новый файл с именем 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; using System.Windows.Controls.Primitives;// Для ButtonBase namespace Notepad1{ partial class Window1 { }}

Первое, что приходит на ум - использовать событие texBox1.TextChanged, в котором проверять состояние флага IsModified и принимать решение о недоступности или доступности источников задачи Save.

  • Добавьте в файл EnabledControls.cs следующий код, регистрирующий еще один обработчик события texBox1.TextChanged
namespace Notepad1{ partial class Window1 { // Вызов размещен в конструкторе класса void AdditionalHandlers() { // Еще один обработчик // обычного события TextChanged txtBox1.TextChanged += EnabledControls_TextChanged; } private void EnabledControls_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e) { // Изменяем состояние интерфейсных элементов _Save itemSave.IsEnabled = btnSave.IsEnabled = IsModified; } }}
  • В файле Window1.xaml.cs добавьте в конструктор класса Window1 последней строкой вызов функции AdditionalHandlers() так
public Window1() { InitializeComponent(); // Создание жестов this.CreateGestures(); // Дополнительные обработчики в файле EnabledControls.cs AdditionalHandlers(); }
  • Запустите приложение - до первого изменения текста кнопки источники Save блокированы, а потом все работает не так. И жест Ctrl+S тоже доступен.

Дело здесь в том, что событие TextChanged срабатывает раньше, чем будет установлен флаг IsModified. Поэтому нужно обрабатывать не событие изменения текста, а событие изменения флага IsModified. Следующим шагом мы преобразуем поле IsModified в свойство на базе нового логического поля modified и создадим свое событие, в обработчике которого и решим управление доступностью источников задачи Save.

  • В файле Window1.xaml.cs найдите объявление поля IsModified и переименуйте его в modified
Было bool IsModified = false; // Флаг изменений содержимого Стало bool modified = false; // Флаг изменений содержимого

Поле modified будет базовым для свойства IsModified. Это все изменения, которые мы вынуждены были провести в прежнем коде. Остальные изменения будем вносить в файл EnabledControls.cs.

  • В файле EnabledControls.cs удалите весь код, связанный с событием texBox1.TextChanged и его обработчиком
namespace Notepad1{ partial class Window1 { // Вызов размещен в конструкторе класса void AdditionalHandlers() { // Еще один обработчик // обычного события TextChanged txtBox1.TextChanged += EnabledControls_TextChanged; } private void EnabledControls_TextChanged(object sender, System.Windows.Controls.TextChangedEventArgs e) { // Изменяем состояние интерфейсных элементов _Save itemSave.IsEnabled = btnSave.IsEnabled = IsModified; } }}
  • Добавьте в файл 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; using System.Windows.Controls.Primitives;// Для ButtonBase namespace Notepad1{ partial class Window1 { // Объявляем внутреннее событие private event EventHandler ChangeModifiedEvent; // Упаковываем базовое поле modified в свойство private bool IsModified { get { return modified; } set { if (modified != value) { modified = !modified; // Инициируем событие, если есть обработчик if (ChangeModifiedEvent != null) ChangeModifiedEvent(this, EventArgs.Empty); } } } // Вызов размещен в конструкторе класса void AdditionalHandlers() { // Начальные запрещения для _Save itemSave.IsEnabled = btnSave.IsEnabled = false; // Удаляем созданный в CreateGestures() жест _Save foreach (KeyGesture gest in gests.Keys) if (gests[gest] == SaveOnExecute) { gests.Remove(gest); break; } // Регистрируем обработчик изменения свойства this.ChangeModifiedEvent += Window1_ChangeModifiedEvent; } void Window1_ChangeModifiedEvent(object sender, EventArgs e) { //MessageBox.Show("Modify"); // Проверяем состояние любого из источников _Save if (btnSave.IsEnabled == false) // Добавляем жест _Save gests.Add(new KeyGesture(Key.S, ModifierKeys.Control), SaveOnExecute);//_Save else // Удаляем жест _Save foreach (KeyGesture gest in gests.Keys) if (gests[gest] == SaveOnExecute) { gests.Remove(gest); break; } // Изменяем состояние интерфейсных элементов _Save itemSave.IsEnabled = btnSave.IsEnabled = IsModified; } }}
  • Запустите приложение и убедитесь, что управление источниками задачи Save, включая жесты, во всех режимах работает как и положено. Разберитесь с кодом!!!

Для управления доступностью других задач приложения нужно построить что-то подобное. Мы этого здесь делать не будем, однако и сейчас уже ясно, что это непростая задача. Для желающих продолжить управление отключениями источников можно посоветовать дополнить файл 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; using System.Windows.Controls.Primitives;// Для ButtonBase namespace Notepad1{ partial class Window1 { // Объявляем внутреннее событие private event EventHandler ChangeModifiedEvent; // Упаковываем базовое поле modified в свойство private bool IsModified { get { return modified; } set { if (modified != value) { modified = !modified; // Инициируем событие, если есть обработчик if (ChangeModifiedEvent != null) ChangeModifiedEvent(this, EventArgs.Empty); } } } // Вызов размещен в конструкторе класса void AdditionalHandlers() { // Регистрируем один и тот же обработчик // всплывающих событий кнопок, элементов меню, // клавиатурных жестов для окна this.AddHandler(ButtonBase.ClickEvent, new RoutedEventHandler(this.Window1_ButtonClick)); this.AddHandler(MenuItem.ClickEvent, new RoutedEventHandler(this.Window1_ItemClick)); this.AddHandler(Keyboard.KeyDownEvent, new RoutedEventHandler(this.Window1_Gesture)); // Дополнительный общий обработчик элементов контекстного меню contextCut.Click += new RoutedEventHandler(item_Context); contextCopy.Click += new RoutedEventHandler(item_Context); contextPaste.Click += item_Context; // Упрощенный синтаксис contextDelete.Click += item_Context; // Начальные запрещения для _Save itemSave.IsEnabled = btnSave.IsEnabled = false; // Удаляем созданный в CreateGestures() жест _Save foreach (KeyGesture gest in gests.Keys) if (gests[gest] == SaveOnExecute) { gests.Remove(gest); break; } // Регистрируем обработчик изменения свойства this.ChangeModifiedEvent += Window1_ChangeModifiedEvent; } void Window1_ChangeModifiedEvent(object sender, EventArgs e) { //MessageBox.Show("Modify"); // Проверяем состояние любого из источников _Save if (btnSave.IsEnabled == false) // Добавляем жест _Save gests.Add(new KeyGesture(Key.S, ModifierKeys.Control), SaveOnExecute);//_Save else // Удаляем жест _Save foreach (KeyGesture gest in gests.Keys) if (gests[gest] == SaveOnExecute) { gests.Remove(gest); break; } // Изменяем состояние интерфейсных элементов _Save itemSave.IsEnabled = btnSave.IsEnabled = IsModified; } private void Window1_ButtonClick(object sender, RoutedEventArgs e) { //MessageBox.Show("Button"); // Повышаем полномочия ссылки Button btn = sender as Button; if (btn == btnSave) { ; } } private void Window1_ItemClick(object sender, RoutedEventArgs e) { //MessageBox.Show("Item"); // Повышаем полномочия ссылки MenuItem item = sender as MenuItem; if (item == itemSave) { ; } } private void Window1_Gesture(object sender, RoutedEventArgs e) { //MessageBox.Show("Key"); } private void item_Context(object sender, RoutedEventArgs e) { //MessageBox.Show("Context"); } }}

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