Упражнение 1. Обработка событий клавиатуры

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

События клавиатурного ввода
Событие Стратегия маршрутизации Описание
PreviewKeyDown Tunnel Происходит при нажатии
KeyDown Bubble Происходит при нажатии
PreviewTextInput Tunnel Происходит для символьных клавиш, когда нажатие завершено и элемент получил символ
TextInput Bubble Происходит для символьных клавиш, когда нажатие завершено и элемент получил символ
PreviewKeyUp Tunnel Происходит при отпускании
KeyUp Bubble Происходит при отпускании

Некоторые элементы сами обрабатывают часть этих событий. Они могут блокировать дальнейшее продвижение некоторых из них или возбуждать дополнительные события. Например, элемент TextBox блокирует событие TextInput, а для клавиш-стрелок блокирует и событие KeyDown, но в то же время возбуждает свое событие TextChanged при изменении содержимого поля ввода.

Построим приложение, которое продемонстрирует обработку клавиатурных событий.

  • Создайте решение EventsAndCommands вместе с новым WPF -проектом KeyEvents командой File/New/Project, для этого настройте окно мастера так


увеличить изображение

Обратите внимание на выбор версии библиотеки .NET Framework 3.0, при более низких версиях шаблоны для WPF станут недоступными. Стартовым новый проект тоже становится автоматически, поскольку он пока первый и единственный в решении. Дерево стартового проекта всегда выделяется в панели Solution Explorer полужирным шрифтом.

  • Заполните файл разметки Window1.xaml следующим дескрипторным кодом
<Window x:Class="KeyEvents.Window1" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window1: События клавиатуры" Height="300" Width="300" MinHeight="300" MinWidth="300" Background="#FFD4D0C8" > <DockPanel LastChildFill="True" Margin="2,2,2,0"> <DockPanel DockPanel.Dock="Top" LastChildFill="True"> <Label DockPanel.Dock="Left" Content="Введите текст:" /> <TextBox Name="textBox" PreviewKeyDown="textBox_KeyEvent" KeyDown="textBox_KeyEvent" PreviewKeyUp="textBox_KeyEvent" KeyUp="textBox_KeyEvent" PreviewTextInput="textBox_PreviewTextInput" /> </DockPanel> <StackPanel DockPanel.Dock="Bottom" Margin="0,5,0,0"> <CheckBox Name="checkIgnoreRepeat" Content="Игнорировать автогенерацию клавиш" IsChecked="True" Click="Check_Click" IsTabStop="False" /> <CheckBox Name="checkIgnorePreviewTextInput" Content="Игнорировать событие PreviewTextInput" IsChecked="True" Margin="0,2" Click="Check_Click" IsTabStop="False" /> <CheckBox Name="checkIgnoreSymbol" Content="Запретить в TextBox нечисловые клавиши" Margin="15,2" Click="Check_Click" IsTabStop="False" /> <CheckBox Name="checkIgnoreOther" Content="Запретить в TextBox некоторые клавиши" Margin="0,2" Click="Check_Click" IsTabStop="False" /> <CheckBox Name="checkConvertNumber" Content="Конвертировать вывод цифровых клавиш" Margin="0,2" Click="Check_Click" IsTabStop="False" /> <Button HorizontalAlignment="Right" Content="Очистить" Margin="0,5,0,2" Padding="5,0,5,0" Click="Button_Click" /> </StackPanel> <ListBox Name="listBox" Focusable="False" /> </DockPanel></Window>
  • Пройдитесь по разметке и командой Navigate to Event Handler контекстного меню для записей событий создайте заготовки обработчиков в файле процедурного кода

Некоторые события связаны с одними и теми же обработчиками, но лишних обработчиков мы таким образом все равно не создадим. Команда Navigate to Event Handler создает новый обработчик только тогда, когда он еще не существуют, а иначе приведет только к позиционированию на уже существующий.

  • Запустите проект и убедитесь, что приведенная разметка реализует следующий интерфейс окна приложения

 

Без создания обработчиков для указанных в разметке событий мы бы не смогли откомпилировать приложение и получить приведенное окно. Если в разметке для какого-то события зарегистрирован обработчик, то перед запуском приложения его нужно обязательно создать, пусть даже с пустым телом. Поскольку компилятор будет настойчиво искать его определение в файле процедурного кода и выдаст ошибку в случае неуспеха. Другое дело, когда обработчик создан, но не присоединен к событию. В таком случае он считается одним из методов класса окна и компилятор протестовать не будет.

  • Изучите код файла разметки, обеспечивающий требуемую компоновку элементов управления пользовательского интерфейса нашего упражнения

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

  • Удалите из файла Window1.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.Navigation;using System.Windows.Shapes; namespace KeyEvents{ public partial class Window1 : Window { public Window1() { InitializeComponent(); textBox.Focus(); } private void textBox_KeyEvent(object sender, KeyEventArgs e) { if ((bool)checkIgnoreRepeat.IsChecked && e.IsRepeat) return;// Игнорировать повторные события // Запретить действие в TextBox некоторых клавиш if(checkIgnoreOther.IsChecked.Value) switch (e.Key) { case Key.Space: // Пробел case Key.Left: // Стрелка влево case Key.Right: // Стрелка вправо case Key.Home: // В начало поля case Key.End: // В конец поля e.Handled = true; break; } string key = e.Key.ToString(); // Конвертируем вывод цифровых клавиш основной клавиатуры if (checkConvertNumber.IsChecked.Value) { KeyConverter converter = new KeyConverter(); key = converter.ConvertToString(e.Key); } string message = e.RoutedEvent.ToString(); message = message.Substring(message.IndexOf('.') + 1); message = String.Format("Event: {0,-25}", message) + "\t Key: " + key; listBox.Items.Add(message); listBox.ScrollIntoView(message);// Видеть последний } private void Button_Click(object sender, RoutedEventArgs e) { // Очищаем поле и список textBox.Clear(); listBox.Items.Clear(); // Возвращаем фокус textBox.Focus(); } private void Check_Click(object sender, RoutedEventArgs e) { // Распознаем и синхронизируем взаимосвязанные CheckBox FrameworkElement checkBox = e.Source as FrameworkElement; switch (checkBox.Name) { case "checkIgnoreSymbol": if (checkIgnoreSymbol.IsChecked.Value) checkIgnorePreviewTextInput.IsChecked = false; break; case "checkIgnorePreviewTextInput": if (checkIgnorePreviewTextInput.IsChecked.Value) checkIgnoreSymbol.IsChecked = false; break; } // Возвращаем фокус textBox.Focus(); } // Для отображаемых символов текстового поля private void textBox_PreviewTextInput(object sender, TextCompositionEventArgs e) { if (checkIgnorePreviewTextInput.IsChecked.Value) return; // Запрещаем в TextBox нечисловые символы short val; // Попытка преобразовать в число без генерации исключения bool success = Int16.TryParse(e.Text, out val); if (!success) { e.Handled = true;// Останавливаем событие } string message = e.RoutedEvent.ToString(); message = message.Substring(message.IndexOf('.') + 1); message = String.Format("Event: {0,-25}", message) + "\t Text: " + e.Text; listBox.Items.Add(message); } }}

В приведенном процедурном коде перехватываются события клавиатуры и в список выводится информация о нажатых клавишах, которая передается вместе с событием в обработчики через объекты их аргументов. Целевым объектом клавиатурных событий в данном коде является элемент текстового поля. Интерфейсные переключатели обеспечивают смену режимов обработки клавиатурных событий и иллюстрируют возможности библиотечных классов.

  • Запустите и испытайте работу приложения, один из рабочих моментов которого может выглядеть так

 

  • Изучите приведенный код процедурного файла, обратите внимание на некоторые приемы программирования

В обработчиках мы извлекаем информацию о событиях как RoutedEvent. Это значит, что события клавиатуры маршрутизируемые и их можно прослушивать и перехватывать не только в целевом объекте, но и в других местах логического дерева элементов. Убедимся в этом:

  • В файле разметки переместите (вырежьте из... и вставьте в...) весь блок регистрации событий из элемента TextBox в самый внешний контейнер DockPanel, чтобы схематично окончательный код стал таким
<Window x:Class="KeyEvents.Window1" ........................................................ > <DockPanel LastChildFill="True" Margin="2,2,2,0" PreviewKeyDown="textBox_KeyEvent" KeyDown="textBox_KeyEvent" PreviewKeyUp="textBox_KeyEvent" KeyUp="textBox_KeyEvent" PreviewTextInput="textBox_PreviewTextInput" > <DockPanel DockPanel.Dock="Top" LastChildFill="True"> <Label DockPanel.Dock="Left" Content="Введите текст:" /> <TextBox Name="textBox" /> </DockPanel> <StackPanel DockPanel.Dock="Bottom" Margin="0,5,0,0"> ..................................................... </StackPanel> <ListBox Name="listBox" Focusable="False" /> </DockPanel></Window>
  • Запустите приложение и убедитесь, что функциональность его осталась прежней

Поставляемый событием в его обработчик объект e класса KeyEventArgs содержит не только информацию о нажатой клавише, но и сведения о состоянии модификаторов (расширителей), таких как Shift, Ctrl, Alt. Свойство e.KeyStates информирует о том, в каком состоянии находилась клавиша в момент генерации события: Down, None, Toggled.

Много полезной информации имеет свойство e.KeyboardDevice, представляющее экземпляр класса KeyboardDevice. Из его свойства e.KeyboardDevice.FocusedElement можно узнать, какой элемент в данный момент имеет фокус клавиатурного ввода. Из свойства e.KeyboardDevice.Modifiers объекта-аргумента обработчика можно определить состояние клавиатурных модификаторов (расширителей клавиатуры), например, так

private void KeyEvent(object sender, KeyEventArgs e) { if ((e.KeyboardDevice.Modifiers & ModifierKeys.Control) == ModifierKeys.Control) Console.WriteLine("Нажато расширение Ctrl"); // Или аналогичный код if((e.KeyboardDevice.Modifiers & ModifierKeys.Alt) > 0) Console.WriteLine("Нажато расширение Alt"); // Или аналогичный код if ((e.KeyboardDevice.Modifiers & ModifierKeys.Shift) != 0) Console.WriteLine("Нажато расширение Shift"); }

Объект e.KeyboardDevice имеет ряд полезных методов для прослушивания состояния любой интересующей нас клавиши

  • public bool IsKeyDown(System.Windows.Input.Key key)
  • public bool IsKeyUp(System.Windows.Input.Key key)
  • public bool IsKeyToggled(System.Windows.Input.Key key)

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

private void KeyEvent(object sender, KeyEventArgs e) { if (e.KeyboardDevice.IsKeyDown(e.Key)) Console.WriteLine("Клавиша {0} нажата", e.Key); if (e.KeyboardDevice.IsKeyUp(e.Key)) Console.WriteLine("Клавиша {0} отпущена", e.Key); // Попутно проверяем if(e.KeyboardDevice.IsKeyToggled(Key.NumLock)) Console.WriteLine("Клавиша NumLock включена"); }

Не только в момент возникновения клавиатурного ввода мы можем получать информацию о состоянии клавиатуры в обработчике, но и в любой интересующий нас момент времени в любом месте программы. Для этого достаточно воспользоваться статическим классом System.Windows.Input. Keyboard, который отслеживает клавиатуру компьютера. По своим возможностям он значительно мощнее класса KeyboardDevice, который мы получаем в обработчике вместе с клавиатурным событием. Вот пример

// Где-то в программе bool Win, Ctrl, Alt, NumLock, CapsLock, ScrollLock; Win = (Keyboard.Modifiers & ModifierKeys.Windows) > 0; Ctrl = (Keyboard.Modifiers & ModifierKeys.Control) > 0; Alt = (Keyboard.Modifiers & ModifierKeys.Alt) > 0; NumLock = Keyboard.IsKeyToggled(Key.NumLock); CapsLock = Keyboard.IsKeyToggled(Key.Capital); ScrollLock = Keyboard.IsKeyToggled(Key.Scroll);







Дата добавления: 2015-04-15; просмотров: 988;


Поиск по сайту:

При помощи поиска вы сможете найти нужную вам информацию.

Поделитесь с друзьями:

Если вам перенёс пользу информационный материал, или помог в учебе – поделитесь этим сайтом с друзьями и знакомыми.
helpiks.org - Хелпикс.Орг - 2014-2024 год. Материал сайта представляется для ознакомительного и учебного использования. | Поддержка
Генерация страницы за: 0.009 сек.