Разработка пользовательского интерфейса на основе технологии Windows Presentation Foundation
кроме цифровых, «–», «,» и Backspace (для обозначения символа, генерируемого клавишей Backspace, в C# можно использовать управляющую последовательность '\b'; нажатие этой клавиши обеспечивает удаление символа, расположенного слева от курсора в активном поле ввода).
Комментарии
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:
Комментарий
При нажатии пробела возникают только события KeyDown и KeyUp (и связанные с ними события PreviewKeyDown и PreviewKeyUp), которые реагируют на нажатие любых клавиш, в том числе и не приводящих к генерации отображаемых символов. Мы перехватываем это событие на уровне родителя обоих