- •Лабораторная работа: События и команды в 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. Разработка простого блокнота с использованием механизма команд
- •Создание нового проекта из копии существующего
- •Краткий анализ задачи
- •Создание и привязка команд
- •Реализация логики доступности источников команд
- •Отображение позиции курсора в строке состояния
Реализация части обработчиков раздела меню Edit
Задачи раздела меню Edit, связанные с использованием буфера обмена, реализовать достаточно легко, поскольку вся необходимая функциональность уже заложена в элементе TextBox. Одними из первых подключим откаты Undo и Redo. Можно было бы создать свой механизм хранения откатов, но мы воспользуемся встроенным в TextBox журналом откатов. Он поддерживается методами Undo() и Redo(). Все члены класса TextBox можно посмотреть в MSDN на русском языке по ссылке
http://msdn.microsoft.com/ru-ru/library/system.windows.controls.textbox_members.aspx
Еще раз вспомним, что мы пока не управляем доступностью элементов-источников задач.
В файле Edit.cs заполните часть обработчиков следующим кодом
partial class Window1
{
//------------------------------------------------------
//
// Обработчики источников задач Edit
//
//------------------------------------------------------
private void UndoOnExecute(object sender, RoutedEventArgs e)
{
txtBox1.Undo();
}
private void RedoOnExecute(object sender, RoutedEventArgs e)
{
txtBox1.Redo();
}
private void CutOnExecute(object sender, RoutedEventArgs e)
{
txtBox1.Cut();
// Вариант
//Clipboard.SetText(txtBox1.SelectedText);
//txtBox1.SelectedText = "";
}
private void CopyOnExecute(object sender, RoutedEventArgs e)
{
txtBox1.Copy();
// Вариант
//Clipboard.SetText(txtBox1.SelectedText);
}
private void PasteOnExecute(object sender, RoutedEventArgs e)
{
// Если в буфере содержатся данные текстового формата
if (Clipboard.ContainsText())
txtBox1.Paste();
}
private void DeleteOnExecute(object sender, RoutedEventArgs e)
{
txtBox1.SelectedText = String.Empty;
}
private void FindOnExecute(object sender, RoutedEventArgs e)
{
}
private void FindNextOnExecute(object sender, RoutedEventArgs e)
{
}
private void ReplaceOnExecute(object sender, RoutedEventArgs e)
{
}
private void GoToOnExecute(object sender, RoutedEventArgs e)
{
}
private void SelectAllOnExecute(object sender, RoutedEventArgs e)
{
txtBox1.SelectAll();
}
}
Оставшиеся обработчики задач меню Edit требуют более сложного кода с использованием немодальных диалоговых окон, которые должны располагаться всегда поверх основного окна с редактируемым текстом. Попробуем последовательно реализовать их.
Разработка и кодирование диалогового окна Find and Replace
Создадим диалоговое окно и назначим ему в качестве владельца основное окно Window1. Окно, имеющее владельца, всегда располагается поверх него, свертывается и закрывается вместе с ним. Именно так и должно вести себя проектируемое окно Find and Replace в режиме runtime (при выполнении).
В панели Solution Explorer выделите узел проекта Notepad1 и командой меню Project/Add Window добавьте новое окно с именем FindAndReplaceDialog
Заполните файл FindAndReplaceDialog.xaml разметкой создания интерфейса диалогового окна
<Window x:Class="Notepad1.FindAndReplaceDialog"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Find and Replace"
WindowStartupLocation="CenterOwner"
SizeToContent="WidthAndHeight"
ResizeMode="NoResize"
ShowInTaskbar="false"
Activated="OnActivated"
Background="{StaticResource DialogBackgroundBrush}"
>
<Grid ShowGridLines="False" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<!-- Контейнер для кнопок -->
<StackPanel
Grid.Row="0"
Grid.RowSpan="2"
Grid.Column="2"
Margin="5"
>
<!-- Задаем отступы кнопок -->
<StackPanel.Resources>
<Style TargetType="{x:Type Button}">
<Setter Property="Margin" Value="3" />
</Style>
</StackPanel.Resources>
<Button
MinWidth="75"
MinHeight="23"
Name="_findNext"
IsDefault="True"
IsEnabled="False"
Click="FindNextClicked"
Content="_Find Next" />
<Button
MinWidth="75"
MinHeight="23"
Name="_replace"
Visibility="Collapsed"
Click="ReplaceClicked"
Content="_Replace" />
<Button
MinWidth="75"
MinHeight="23"
Name="_replaceAll"
Click="ReplaceAllClicked"
Visibility="Collapsed"
Content="Replace _All" />
<Button
MinWidth="75"
MinHeight="23"
Click="CancelClicked"
IsCancel="True"
Content="Cancel" />
</StackPanel>
<!-- Текстовые метки и текстовые поля в левом верхнем квадранте -->
<Grid
ShowGridLines="False"
Grid.Row="0"
Grid.Column="0"
Margin="5 10 0 0" >
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Label
Grid.Row="0"
Grid.Column="0"
VerticalAlignment="Center"
Target="{Binding ElementName=_findWhat}"
Content="Fi_nd what:" />
<TextBox
Name="_findWhat"
Grid.Row="0"
Grid.Column="1"
Width="150"
MaxLength="100"
Margin="0 5 0 5"
AcceptsReturn="False"
TextChanged="FindTextChanged" />
<Label
Name="_replaceLabel"
Grid.Row="1"
Grid.Column="0"
VerticalAlignment="Center"
Target="{Binding ElementName=_replaceWith}"
Visibility="Collapsed"
Content="Re_place with:" />
<TextBox
Name="_replaceWith"
Grid.Row="1"
Grid.Column="1"
Width="150"
MaxLength="100"
VerticalAlignment="Center"
Visibility="Collapsed"
AcceptsReturn="False" />
</Grid>
<!-- Секция установки регистра и направления поиска для замены -->
<Grid
Grid.Row="1"
Grid.Column="0"
>
<Grid.ColumnDefinitions>
<ColumnDefinition />
<ColumnDefinition />
</Grid.ColumnDefinitions>
<!-- Установка регистра -->
<CheckBox
Name="_matchCase"
Grid.Column="0"
Margin="8 0 10 10"
HorizontalAlignment="Left"
VerticalAlignment="Bottom"
IsChecked="False"
Focusable="True"
Content="Match _case" />
<!-- Группа радиокнопок направления поиска -->
<GroupBox
Name="_directionGroupBox"
Header="Direction"
Grid.Column="1"
HorizontalAlignment="Right"
Margin="0 10 0 10">
<StackPanel Orientation="Horizontal" Margin="10 20 10 10">
<RadioButton
Name="_findDown"
IsChecked="True"
Content="_Down" />
<RadioButton
Name="_findUp"
Margin="8 0 0 0"
Content="_Up" />
</StackPanel>
</GroupBox>
</Grid>
</Grid>
</Window>
В настройках окна мы устанавливаем цвет фона из статического ресурса параметром
Background="{StaticResource DialogBackgroundBrush}"
Обратите внимание и на другие настройки интерфейса окна и элементов
Добавьте в ресурсы приложения файла App.xaml следующий код
<Application x:Class="Notepad1.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
StartupUri="FindAndReplaceDialog.xaml">
<Application.Resources>
<String xmlns="clr-namespace:System;assembly=Mscorlib" x:Key="ApplicationTitle1">
"Window1: Управление состоянием источников команд"
</String>
<String xmlns="clr-namespace:System;assembly=Mscorlib" x:Key="ApplicationTitle2">
"Window2: Управление состоянием источников команд"
</String>
<SolidColorBrush x:Key="DialogBackgroundBrush"
Color="{x:Static SystemColors.ControlColor}" />
</Application.Resources>
</Application>
Обратите внимание, что временно для просмотра интерфейса в режиме выполнения мы назначили диалоговое окно стартовым окном приложения. Чуть позже мы вернем класс Window1 в категорию стартовых, а пока нам нужно просто посмотреть, как выглядит созданный интерфейс.
Запустите приложение Notepad1 - начальный интерфейс для задачи Find должен быть таким
В настройках элементов разметки, связанных с задачей Replace, мы использовали значение атрибута Visibility=" Collapsed ". Есть еще значения Visibility=" Hidden " и Visibility=" Visible " (по умолчанию). Hidden - элемент скрыт (не отображается, но занимает место), Collapsed - элемент свернут (не отображается и не занимает место).
Заполните кодовую часть в файле FindAndReplaceDialog.xaml.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.Shapes;
namespace Notepad1
{
public partial class FindAndReplaceDialog : Window
{
public FindAndReplaceDialog()
{
InitializeComponent();
// Начальная доступность кнопок, хотя кнопки Replece
_findNext.IsEnabled = _replace.IsEnabled = _replaceAll.IsEnabled
= !String.IsNullOrEmpty(_findWhat.Text);
}
#region Открытые свойства - обертки закрытых полей
//------------------------------------------------------
//
// Открытые свойства - обертки закрытых полей
//
//------------------------------------------------------
// Содержимое текстового поля _findWhat
public string FindWhat
{
get
{
return _findWhat.Text;
}
set
{
_findWhat.Text = value;
}
}
// Содержимое текстового поля _replaceWith
public string ReplaceWith
{
get
{
return _replaceWith.Text;
}
set
{
_replaceWith.Text = value;
}
}
// Определение состояния флага учета регистра
public bool? MatchCase { get { return _matchCase.IsChecked; } }
// Определение состояния радиокнопки направления поиска назад
public bool? SearchUp { get { return _findUp.IsChecked; } }
// Управление видимостью интерфейса замены
// Hidden - элемент скрыт (не отображается, но занимает место)
// Collapsed - элемент свернут (не отображается и не занимает место)
public bool ShowReplace
{
get { return _replaceWith.Visibility == Visibility.Visible; }
set
{
Visibility show;
if (value)
{
// Отобразить
show = Visibility.Visible;
_directionGroupBox.Visibility = Visibility.Collapsed;
_findDown.IsChecked = true;
}
else
{
// Свернуть
show = Visibility.Collapsed;
_directionGroupBox.Visibility = Visibility.Visible;
}
_replaceLabel.Visibility = _replaceWith.Visibility =
_replace.Visibility = _replaceAll.Visibility = show;
}
}
#endregion Общедоступные свойства - обертки полей
#region Открытые события для обработки в основном классе
//------------------------------------------------------
//
// Открытые события для обработки в основном классе Window1
// Обеспечивают взаимодействие диалогового окна с владельцем
//
//------------------------------------------------------
// Объявляем немаршрутизованные события
public event EventHandler FindNext;
public event EventHandler Replace;
public event EventHandler ReplaceAll;
#endregion Открытые события для обработки в основном классе Window1
#region Закрытые обработчики
//------------------------------------------------------
//
// Закрытые методы
// При возбуждении событий первый параметр - ссылка на диалог,
// которую в основном классе приведем к самому диалоговому окну
//
//------------------------------------------------------
void OnActivated(object sender, EventArgs e)
{
_findWhat.Focus();
}
private void FindNextClicked(object sender, RoutedEventArgs e)
{
// Если на событие подписались, возбуждаем его
if (FindNext != null)
{
FindNext(this, EventArgs.Empty);
}
}
private void ReplaceClicked(object sender, RoutedEventArgs e)
{
// Возбуждаем событие, если для него существует обработчик
if (Replace != null)
{
Replace(this, EventArgs.Empty);
}
}
private void ReplaceAllClicked(object sender, RoutedEventArgs e)
{
// Проверяем наличие обработчика и возбуждаем событие
if (ReplaceAll != null)
{
ReplaceAll(this, EventArgs.Empty);
}
}
private void CancelClicked(object sender, RoutedEventArgs e)
{
this.Close();
}
private void FindTextChanged(object sender, TextChangedEventArgs e)
{
// Управление доступность кнопок в зависимости от текстового поля
_findNext.IsEnabled = _replace.IsEnabled = _replaceAll.IsEnabled
= !String.IsNullOrEmpty(_findWhat.Text);
}
#endregion Закрытые обработчики
}
}
При щелчке на кнопках собственные обработчики кнопок возбуждают в диалоге события, которые будет прослушивать основное окно, перехватывать их и обрабатывать своими обработчиками. При объявлении свойств MatchCase и SearchUp использован тип " bool?", поскольку элементы CheckBox и RadioButton имеют три состояния: IsChecked=" False ", IsChecked=" True " и IsChecked=" {x:Null} ". Для выборки только булевых состояний при программном управлении в клиентском коде этими элементами через открытые свойства нужно будет применять явное приведение типов. Например, caseFlag=(bool)_dlg.MatchCase;, где _dlg - это экземпляр класса FindAndReplaceDialog в клиенте.
Запустите приложение для проверки отсутствия синтаксических ошибок
