- •Лабораторная работа: События и команды в 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. Разработка простого блокнота с использованием механизма команд
- •Создание нового проекта из копии существующего
- •Краткий анализ задачи
- •Создание и привязка команд
- •Реализация логики доступности источников команд
- •Отображение позиции курсора в строке состояния
Подключение иконки приложения
Скопируйте из папки Source в корень проекта Notepad1 командой контекстного меню Add/Existing Item иконку Notepad.ico и подключите ее к окну Window1
<Window x:Class="Notepad1.Window1"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Window1: Управление состоянием источников команд"
Width="500" Height="375"
MinWidth="500" MinHeight="375"
WindowStartupLocation="CenterScreen"
ResizeMode="CanResizeWithGrip"
Loaded="Window_Loaded"
Icon="Notepad.ico"
>
......................................................
</Window>
Распределение класса по нескольким файлам и создание вспомогательных функций
Код решения поставленной задачи будет достаточно большим. Чтобы сделать его обозримым, распределим отдельные группы частичного класса по отдельным файлам в соответствии с разделами меню. Все равно компилятор их увидет и соберет в единую сборку.
В панели Solution Explorer выделите узел проекта Notepad1 и командой Project/Add Class добавьте три файла с именами File.cs, Edit.cs и Other.cs
Скопируйте из файла Window1.xaml.cs в каждый из этих новых файлов код подключения пространств имен и отредактируйте заготовки частей класса Window1, которые пока будут одинаковые, так
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 Notepad1
{
partial class Window1
{
}
}
Откройте файл Window1.xaml.cs и дополните класс Window1 вспомогательным кодом, после чего он должен стать таким
using System;
using System.Windows;
using System.Windows.Input;
using Microsoft.Win32; // Для стандартных диалогов Win32
using System.IO; // Работа с файлами и каталогами
namespace Notepad1
{
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
// Отключаем в TextBox встроенный жест Ctrl+X для команды Cut
KeyBinding keyBinding = new KeyBinding(
ApplicationCommands.NotACommand, Key.X, ModifierKeys.Control);
txtBox1.InputBindings.Add(keyBinding);
// Отключаем в TextBox встроенный жест Ctrl+C для команды Copy
keyBinding = new KeyBinding(
ApplicationCommands.NotACommand, Key.C, ModifierKeys.Control);
txtBox1.InputBindings.Add(keyBinding);
// Отключаем в TextBox встроенный жест Ctrl+V для команды Paste
keyBinding = new KeyBinding(
ApplicationCommands.NotACommand, Key.V, ModifierKeys.Control);
txtBox1.InputBindings.Add(keyBinding);
}
#region private Fields - локальные поля
//------------------------------------------------------
//
// private Fields - локальные поля
//
//------------------------------------------------------
bool IsModified = false;// Флаг изменений содержимого
string strLoadedFile; // Полное имя загруженного документа
#endregion private Fields
#region Auxiliary Methods - вспомогательные методы
//------------------------------------------------------
//
// Auxiliary Methods - вспомогательные методы
//
//------------------------------------------------------
// Метод возвращает true, если содержимое
// TextBox не требует сохранения
bool flag;
bool CheckModifiedAndSaveIt()
{
if (!IsModified)
return true;
MessageBoxResult result =
MessageBox.Show(
"Сохранить изменения?", "", // Контекст и заголовок
MessageBoxButton.YesNoCancel, // Кнопки диалога
MessageBoxImage.Question, // Иконка вопроса
MessageBoxResult.Yes // Кнопка с фокусом
);
switch (result)
{
case MessageBoxResult.Yes:
if (String.IsNullOrEmpty(strLoadedFile))
flag = DisplaySaveDialog(""); // Запись с диалогом
else
flag = SaveFile(strLoadedFile); // Просто запись
break;
case MessageBoxResult.No:
flag = true;
break;
case MessageBoxResult.Cancel:
flag = false;
break;
}
return flag;
}
// Вызывает диалоговое окно записи файла
// и возвращает true, если файл был сохранен
bool DisplaySaveDialog(string strFileName)
{
SaveFileDialog dlg = new SaveFileDialog();
dlg.Filter = "Text Documents(*.txt)|*.txt|All Files(*.*)|*.*";
dlg.FileName = strFileName;
bool result = (bool)dlg.ShowDialog(this); // Желание пользователя
if (result)
result = SaveFile(dlg.FileName); // Возможность компьютера
return result;
}
// Сохраняет документ и возвращает true при успехе
// Аргумент - полное имя файла
bool SaveFile(string strFileName)
{
try
{
File.WriteAllText(strFileName, txtBox1.Text,
System.Text.Encoding.GetEncoding(1251));
}
catch (Exception e)
{
// Ловим все исключения и выводим диалог
MessageBox.Show(
"Ошибка записи файла:\n" + e.Message, "",
MessageBoxButton.OK,
MessageBoxImage.Asterisk
);
return false;
}
strLoadedFile = strFileName;
UpdateTitle(); // Меняем заголовок окна
IsModified = false; // Нет изменений текста
return true;
}
// Диалог открытия файла возвращает true при успехе
bool DisplayOpenDialog()
{
flag = CheckModifiedAndSaveIt(); // Проверяем и сохраняем изменения
if(!flag)
return flag;
OpenFileDialog dlg = new OpenFileDialog();
dlg.Filter = "Text Documents(*.txt)|*.txt|All Files(*.*)|*.*";
bool result = (bool)dlg.ShowDialog(this); // Желание пользователя
if (result)
result = OpenFile(dlg.FileName); // Возможность компьютера
return result;
}
// Открывает файл и при успехе возвращает true
bool OpenFile(string strFileName)
{
try
{
txtBox1.Text = File.ReadAllText(strFileName,
System.Text.Encoding.GetEncoding(1251));
}
catch (Exception e)
{
// Ловим все исключения и выводим диалог
MessageBox.Show(
"Ошибка чтения файла:\n" + e.Message, "",
MessageBoxButton.OK,
MessageBoxImage.Asterisk
);
return false;
}
strLoadedFile = strFileName;
UpdateTitle(); // Меняем заголовок окна
IsModified = false; // Нет изменений текста
// Сбрасываем границы выделенного текста поля редактирования
txtBox1.SelectionStart = 0;
txtBox1.SelectionLength = 0;
return true;
}
// Коррекция заголовка окна
void UpdateTitle()
{
// Извлекаем заголовок окна из словаря ресурсов
String title = Application.Current.
Resources["ApplicationTitle1"].ToString();
//if (strLoadedFile == null || strLoadedFile.Trim() == String.Empty)
if (String.IsNullOrEmpty(strLoadedFile)) // Проще!
{
this.Title = "Untitled - " + title;
return;
}
// Извлекаем имя файла из полного пути
int startIndex = strLoadedFile.LastIndexOf('\\') + 1;
int endIndex;
// Проверяем в системном реестре настройки системы по скрытию расширения файлов
using (RegistryKey filekey = Registry.CurrentUser.CreateSubKey(
@"Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced"))
{
if ((filekey != null) && (filekey.GetValue("HideFileExt", 0).ToString() == "0"))
{
endIndex = strLoadedFile.Length; // Нет расширения
}
else
{
endIndex = strLoadedFile.LastIndexOf('.'); // Отсекаем расширение
}
}
if (endIndex > startIndex)
{
this.Title = strLoadedFile.Substring(startIndex) +
" - " + title;
}
else
{
this.Title = strLoadedFile.Substring(startIndex, endIndex - startIndex) +
" - " + title;
}
}
#endregion Auxiliary Methods
}
}
Смысл добавленного кода подробно помечен коментариями и следует его внимательно изучить. Ключевым полем работы кода является флаг IsModified, сигнализирующий об изменении содержимого элемента TextBox. Поднятие флага выполним в обработчике события TextChanged этого элемента.
В разметку TextBox добавьте регистрацию обработчика события TextChanged и создайте сам обработчик
<!-- Многострочное текстовое поле редактирования -->
<TextBox TextWrapping="Wrap"
AcceptsReturn="True"
AcceptsTab="True"
VerticalScrollBarVisibility="Auto"
Name="txtBox1"
TextChanged="txtBox1_TextChanged"
>
....................................................
</TextBox>
Обработчик txtBox1_TextChanged() заполните так
private void txtBox1_TextChanged(object sender,
System.Windows.Controls.TextChangedEventArgs e)
{
if (IsModified)
return;
else
IsModified = true;
}
