3.4. Использование обработчика событий от клавиатуры

Определите обработчик события PreviewTextInput для MainWindow:

Кроме того, измените метод button1_Click следующим образом:

screen_image_57_465_75

Результат. Теперь для ввода любой операции достаточно нажать соответствующую клавишу (поскольку клавиша «–» может использоваться для ввода отрицательных чисел, в качестве ускорителя для кнопки «–» выбрана комбинация Shift+«–», соответствующая символу подчеркивания «_»). При вводе чисел игнорируются все клавиши, кроме цифровых, «–», «,» и Backspace (для обозначения символа, генерируемого клавишей Backspace, в C# можно использовать управляющую последовательность ''; нажатие этой клавиши обеспечивает удаление символа, расположенного слева от курсора в активном поле ввода).

Комментарии

1. При реализации описанных возможностей мы воспользовались тем, что событие PreviewTextInput является туннелируемым, т. е. вначале оно обрабатывается в родительском компоненте верхнего уровня (окне), а затем «спускается» по иерархии подчинения к тому компоненту, в котором возникло. Это позволило уже на уровне окна проанализировать введенный текст и выполнить требуемые действия по изменению арифметической операции (и, кроме того, «не пропустить» дальше те символы, которые не имеет смысла использовать в арифметических операндах). Напомним, что для прекращения последующих вызовов обработчиков данного события необходимо пометить событие как обработанное, положив свойство e.Handled равным true. Следует сказать, что пометка события как обработанного позволяет отменить вызов и стандартных обработчиков событий, связанных с компонентами (в нашем случае был отменен вызов стандартного обработчика события TextInput, обеспечивающего добавление набранного на клавиатуре символа в поле ввода).

2. Для имитации возникновения события, связанного с нажатием кнопок 1–4, в методе Window_PreviewTextInput вызывается обработчик данного события button1_Click. При этом необходимо указать нужную кнопку. Проще всего передать ее в качестве первого параметра обработчика, однако такой подход требует корректировки действий, содержащихся в методе button1_Click.

В операторе, добавленном в метод button1_Click, делается попытка привести параметр sender к типу Button. Если эта попытка успешна, то соответствующая кнопка помещается в переменную s. Если же указанное преобразование нельзя выполнить, то операция as возвращает значение null. Это означает, что обработчик был вызван родительским компонентом, а «истинный» адресат события содержится в свойстве e.Source, которое в этом случае приводится к типу Button и сохраняется в переменной s. Все описанные действия удалось реализовать в единственном операторе благодаря операции a ?? b, которая возвращает значение a, если оно не равно null, и b в противном случае.

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

Это связано с тем, что пробел в WPF-приложениях обрабатывается особым образом: несмотря на то, что он является отображаемым символом и, казалось бы, нажатие на него должно приводить к возникновению события TextInput (и предшествующего ему события PreviewTextInput), этого не происходит. Таким образом, если мы хотим заблокировать ввод пробелов, это придется сделать с помощью дополнительного обработчика.

Исправление. Определите для компонента StackPanel, содержащего поля ввода, обработчик события PreviewKeyDown:

screen_image_59_170_75

Комментарий

При нажатии пробела возникают только события KeyDown и KeyUp (и связанные с ними события PreviewKeyDown и PreviewKeyUp), которые реагируют на нажатие любых клавиш, в том числе и не приводящих к генерации отображаемых символов. Мы перехватываем это событие на уровне родителя обоих полей ввода, поэтому оно не доходит до них и пробелы в полях ввода не отображаются.

Заметим, что перехватывать событие на более высоком уровне (на уровне вертикальной панели StackPanel или на уровне окна), не следует, так как в этом случае нажатие пробела не дойдет и до других компонентов окна, в частности, до кнопок. В результате станет недоступной возможность нажать кнопку, выделив ее и нажав клавишу пробела.

Недочет 2. В нашей программе предполагается, что десятичным разделителем является запятая, тогда как при других региональных настройках в системе Windows может использоваться другой разделитель.

Исправление. Измените фрагмент последнего оператора в методе Window_PreviewTextInput:

Комментарий

Статическое свойство CurrentCulture класса CultureInfo, определенного в пространстве имен System.Globalization, позволяет получить информацию о региональных настройках, используемых операционной системой, в частности о числовых форматах. Необходимость в указании индекса [0] обусловлена тем, что свойство NumberDecimalSeparator имеет строковый тип, который не совместим по присваиванию с символьным типом. Заметим, что свойство NumberDecimalSeparator доступно только для чтения, однако имеется возможность изменить региональные настройки в целом для конкретного приложения (см. по этому поводу комментарий в проекте CLOCK, п. 4.1).

Более 800 000 книг и аудиокниг! 📚

Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением

ПОЛУЧИТЬ ПОДАРОК

Данный текст является ознакомительным фрагментом.