Залепа №12. Microsoft не знает собственного кода.
Безусловно, чем больше различных свойств, методов и событий предусмотрели для класса его создатели, тем более гибок он будет в использовании. По крайней мере так должно быть. По крайней мере в теории. Но практика вещь суровая.
Обратим свое внимание на набор обрабатываемых любым контролом событий, связанных с клавиатурой. Типичный TextBox содержит 4 штуки: KeyDown, KeyPress, KeyUp и PreviewKeyDown. Последнее оставим для другого раза, а вот первые три изучим поподробнее.
KeyDown и KeyUp срабатывают соответственно при нажатии и отпускании клавиш. При этом в обработчик передается вся необходимая для точного определения клавиши и состояния клавиатуры информация. Даже с избытком, некоторые свойства просто дублируют другие. Ну да ладно.
Недостатком здесь служит тот факт, что обработчик получает коды клавишей, а не коды символов. Соответственно они никак не зависят от состояния "включенности" CapsLock и NumLock, равно как и от текущей раскладки (языка ввода). Это приводит к серьезным проблемам при написании программ работы с текстом, которым нужны не коды клавиш, а именно коды набираемых пользователем символов.
Для решения проблемы разработчики и ввели событие OnKeyPress. Фактически оно наступает, когда в буфер клавиатуры попадает очередной символ и обработчик этого события получает как раз код символа, а не клавиши. Соответственно это событие не наступает для нажатий/отпусканий таких клавиш как Shift, Alt, Ctrl, F1-F12, Tab и т.д.
Стоит сказать еще об одной вещи. И в обработчике OnKeyDown и в OnKeyPress существует (слава Богу!) возможность указать системе, что принятый символ не должен проходить дальнейшую обработку и должен покинуть очередь вода. Таким образом можно "фильтровать базар", т.е. отсеивать из вводимого пользователем бреда то, что заведомо ложно. Например, если требуется ввести сумму, то нужно из потока ввода удалить все символы кроме цифр, точки, минуса и возможно кода клавиши "Забой" (BackSpace).
Так вот, суть сегодняшнего прикола в примере из MSDN, в котором бравые парни из мега-крутой конторы в очередной раз показывают как нужно правильно пользоваться их библиотекой. Вот этот пример со всеми комментариями:
// Boolean flag used to determine when a
// character other than a number is entered.
private bool nonNumberEntered = false;
// Handle the KeyDown event to determine the
// type of character entered into the control.
private void textBox1_KeyDown(object sender, KeyEventArgs e)
{
// Initialize the flag to false.
nonNumberEntered = false;
// Determine whether the keystroke is a number from
// the top of the keyboard.
if (e.KeyCode < Keys.D0 || e.KeyCode > Keys.D9)
{
// Determine whether the keystroke is a number
// from the keypad.
if (e.KeyCode < Keys.NumPad0 || e.KeyCode > Keys.NumPad9)
{
// Determine whether the keystroke is a backspace.
if(e.KeyCode != Keys.Back)
{
// A non-numerical keystroke was pressed.
// Set the flag to true and evaluate
// in KeyPress event.
nonNumberEntered = true;
}
}
}
}
// This event occurs after the KeyDown event and
// can be used to prevent characters from entering
// the control.
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
// Check for the flag being set in the KeyDown event.
if (nonNumberEntered == true)
{
// Stop the character from being entered into
// the control since it is non-numerical.
e.Handled = true;
}
}
Как видно из примера, для такой простой вещи, как удаление из потока заведомо лишних символов, требуется писать обработчики для сразу ДВУХ событий, да еще и переменную использовать. Иначе нельзя - работать наверное не будет. По крайней мере они другого варианта не узрели. Кстати вложенные сравнения им тоже видно не по зубам. :)
Ладно, вот еще вариант, на этот раз мой:
private void textBox1_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
{
if (!((e.KeyCode >= Keys.D0 && e.KeyCode <= Keys.D9) ||
(e.KeyCode >= Keys.NumPad0 && e.KeyCode <= Keys.NumPad9) ||
e.KeyCode == Keys.Separator || e.KeyCode == Keys.Decimal ||
e.KeyCode == Keys.Back || e.KeyCode == Keys.Subtract))
e.SuppressKeyPress = true;
}
Результат работы обоих примеров одинаковый, только у меня переопределен только один обработчик, не нужна дополнительная переменная, да еще и ввод символа "минус" разрешен, равно как и десятичной точки и десятичной запятой. :)
Вывод этого краткого поста таков:
- разработчики библиотеки до такой степени напутали с лишними свойствами и методами, что сами потерялись в этой головоломке;
- из-за разработчиков библиотеки, которые "забыли" дополнить TextBox возможностью фильтровать (хотя бы на уровне "цифра/буква") пользовательский ввод, нам с вами приходится делать финты ушами, чтобы реализовать такие полезные в каждодневной работе вещи.
О MaskedTextBox прошу мне не напоминать - с глюками этого контрола разберемся отдельно как-нибудь позже.