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

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

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

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


Комментарий

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

3.5. Контроль за изменением исходных данных

Добавьте в метод button1_Click следующий оператор:



Кроме того, определите для поля ввода textBox1 обработчик события TextChanged, а также свяжите этот обработчик с полем ввода textBox2:




Результат. При изменении операции или содержимого текстовых полей результат предыдущего вычисления стирается. Это важная возможность, позволяющая предотвратить рассогласование отображаемых данных. При ее отсутствии возможна ситуация, когда после выполнения, например, вычислений вида 3 + 2 (с результатом 5), пользователь изменит первый операнд на 2, получив на экране текст 2 + 2 = 5.

Комментарии

1. Здесь мы использовали более традиционный способ связывания одного обработчика события с несколькими компонентами – путем указания этого обработчика в xaml-файле в описании каждого компонента.

Впрочем, в данном случае тоже можно было воспользоваться механизмом маршрутизируемых событий и после создания обработчика textBox1_TextChanged для компонента textBox1 не копировать соответствующий атрибут в компонент textBox2, а переместить его в родительский компонент StackPanel, снабдив префиксом TextBox:


2. Указание обработчиков событий, подобных событию TextChanged, непосредственно в xaml-файле может приводить к неожиданным ошибкам. Например, если закомментировать условный оператор в методе textBox1_TextChanged

то при запуске программы возникнет исключение NullReferenceException («Ссылка на объект не указывает на экземпляр объекта»). Это связано с тем, что в WPF событие TextChanged возникает сразу после конструирования поля ввода (при присваивания свойству Text начального значения). Но в момент создания поля ввода textBox1 метка label2 еще не существует, поскольку компоненты создаются в порядке их указания в xaml-файле, что и приводит к возникновению исключения.

Мы избежали этой ошибки, добавив проверку в обработчик. Исправить подобную ошибку можно и другим способом: не добавляя проверку в метод textBox1_TextChanged, удалить оба атрибута TextChanged="textBox1_TextChanged" в xaml-файле и вместо этого добавить