Лекция 9 4.5. Проверка данных, вводимых пользователем Посредством интерфейса пользователи передают приложению необходимую для его работы информацию. Проверка данных является важным фактором защиты от некорректного ввода, а также способствует дружественности интерфейса пользователя. Для реализации такой проверки имеются свойства и события элементов управления, принимающих данные, а также методы передачи фокуса ввода. Более того, в стандартной библиотеке имеется специальный компонент – ErrorProvider, предназначенный для отображения сообщений с описанием допущенной пользователем ошибки. Разработчик располагает двумя вариантами проверки вводимой информации: на уровне поля и на уровне формы. На уровне поля данные проверяются для каждого поля в отдельности, по мере его заполнения. Проверка на уровне формы выполняется после того, как пользователь заполнит все ее поля. Предположим, что требуется заполнить имя, адрес и номер телефона. На уровне формы все перечисленные поля можно проверить одновременно, например, после нажатия кнопки ОК. 4.5.1. Проверка на уровне поля 4.5.1.1. Свойства, ограничивающие ввод Иногда необходимо проверять данные сразу же после (или даже во время) их ввода. Чаще всего для приема данных от пользователя применяется элемент управления TextBox. Рассмотрим некоторые из его свойств, позволяющие ограничивать вводимые значения или их просмотр. public int MaxLength; 2 Ограничивает количество символов, вводимых в текстовое поле. С помощью этого свойства удобно создавать поля значений фиксированной длины, например, почтовых индексов. public char PasswordChar; Скрывает от посторонних глаз вводимое значение. Заданный символ отображается вместо вводимых пользователем символов. Это свойство обычно используется для защиты паролей. Независимо от назначенного символа, свойство Text содержит фактически введенное значение. public bool ReadOnly; Определяет, разрешено ли пользователю редактировать текстовое поле. public bool MultiLine; Задает, может ли поле ввода состоять из нескольких строк. Введенные строки сохраняются в виде строкового массива в наборе TextBox.Lines. 4.5.1.2. Клавиатурные события Обработка клавиатурных событий на уровне поля позволяет немедленно проверять вводимые данные. Элементы управления, принимающие ввод с клавиатуры, генерируют следующие три события: public delegate void System.Windows.Forms.KeyEventHandler (object sender, KeyEventArgs e); public delegate void System.Windows.Forms.KeyPressEventHandler (object sender, KeyPressEventArgs e); public event KeyEventHandler KeyDown, KeyUp; public event KeyPressEventHandler KeyPress; Нажатие и освобождение любой клавиши сопровождается генерацией события KeyDown и KeyUp соответственно. Источником событий является элемент управления, обладающий фокусом ввода – текущий элемент. Он передает сведения о нажатой (или отпущенной) клавише (или сочетании клавиш) через экземпляр класса KeyEventArgs. События KeyDown и KeyUp часто используются для определения, нажаты ли клавиши Alt, Ctrl или Shift. 3 Информация доступна обработчику в параметре e. Его свойства Alt, Ctrl и Shift возвращают значения типа bool, указывающие на нажатие соответствующих клавиш. Ниже показан пример обработчика события KeyUp, проверяющего нажатие клавиши Alt: private void textBox1_KeyUp (object sender, KeyEventArgs e) { if (e.Alt) MessageBox.Show ("Нажата клавиша ALT"); } Свойство KeyEventArgs.KeyCode позволяет определить, какая именно клавиша вызвала событие. Оно возвращает код клавиши. Ниже показан пример обработчика, отображающего сообщение с кодом нажатой клавиши: private void textBox1_KeyDown(object sender, KeyEventArgs e) { MessageBox.Show ( e.KeyCode.ToString() ); } При нажатии символьной клавиши текущий элемент управления генерирует событие KeyPress. К таким клавишам относятся алфавитноцифровые, а также ряд специальных (Enter, Backspace и др.). Если при нажатии клавиш не генерируется символ, событие KeyPress не происходит. К последним клавишам относятся модификаторы Ctrl, Alt, Shift, а также функциональные клавиши. При генерации KeyPress обработчик получает экземпляр класса KeyPressEventArgs, свойство KeyChar которого содержит код соответствующего символа клавиши. Как говорилось в п. 3.5, тип char поддерживает статические методы для проверки символов. Их можно применять в обработчике события KeyPress: private void textBox1_KeyPress (object sender, KeyPressEventArgs e) { if ( char.IsDigit(e.KeyChar) ) MessageBox.Show ("Вы нажали клавишу с цифрой"); } 4 4.5.1.3. Работа с фокусом ввода В каждый момент фокус ввода на форме имеется только у одного элемента. Программно передать фокус элементу можно его методом public bool Focus (); Возвращается булево значение, свидетельствующее об успешной или неудачной передаче. Запрещенные или невидимые элементы не могут получать фокус. Определить эту возможность позволяет свойство элемента public bool CanFocus; Существуют несколько событий элемента управления, связанных с передачей фокуса, генерируемых в указанном порядке: public event EventHandler Enter, GotFocus, Leave; public event CancelEventHandler Validating; public event EventHandler Validated, LostFocus; События Enter и Leave генерируются, когда фокус переходит к элементу управления (но еще не получен им) и соответственно покидает его. События GotFocus и LostFocus происходят непосредственно при состоявшихся получении и потере фокуса элементом управления. Теоретически эти события можно применять для проверки вводимых значений на уровне поля, однако события Validating и Validated лучше подходят для этой цели. Удобнее проверять вводимые данные при помощи события Validating. Оно генерируется, если у элемента управления, который получит фокус следующим, свойство CausesValidation равно true. Кроме того, использование Validating требует, чтобы у самого проверяемого элемента свойство CausesValidation также было равно true. У всех элементов, созданных во время разработки, свойство CausesValidation установлено в true по умолчанию, исключение обычно составляет лишь кнопка Help. Обработчик Validating может проверить соответствие введенного значения некоторому формату и при необходимости запретить передачу фокуса другому элементу управления. Для этого обработчику в качестве 5 параметра передается экземпляр CancelEventArgs с булевым свойством Cancel (см. аналогию с событием формы Closing). Событие Validated генерируется после успешной проверки введенного значения для элемента управления и позволяет выполнить некоторые действия в зависимости от результатов проверки. Ниже показан пример обработчика события Validating, который не разрешает передать фокус следующему элементу управления, пока пользователь не введет значение в поле TextBoxl. private void textBox1_Validating (object sender, CancelEventArgs e) { e.Cancel = (textBoxl.Text == ""); } 4.5.2. Проверка на уровне формы Проверка данных на уровне формы позволяет одним алгоритмом проанализировать содержимое всех ее полей. Для этого часто применяют процедуру, которая вызывается при уходе пользователя с текущей формы; другой способ – обработка события специального элемента на форме. Рассмотрим пример метода, проверяющего данные на уровне формы. По щелчку кнопки btnValidate этот метод проверяет, все ли текстовые поля формы заполнены. Если обнаружено пустое поле, метод передает ему фокус. private void btnValidate_Click (object sender, EventArgs e) { foreach (Control aControl in this.Controls) { if ( aControl is TextBox && aControl.Text == "") { aControl.Focus (); return; } } } Обработка клавиатурных событий на уровне формы – более сложная методика. Она позволяет централизованно управлять вводом данных в совокупность полей формы. Можно написать метод, активирующий 6 командные кнопки только после заполнения всех полей и выполняющий определенные действия в зависимости от того, какие клавиши нажимаются. По умолчанию форма генерирует события клавиатуры KeyPress, KeyDown и KeyUp только в случае, только если на ней нет активированных или видимых элементов управления. В противном случае эти события генерирует элемент управления, имеющий фокус. Чтобы заставить форму автоматически генерировать события клавиатуры, необходимо установить ее свойство KeyPreview в true. В результате форма будет генерировать эти события прежде элемента управления, получившего фокус. 4.5.3. Оповещение пользователя об ошибках ввода Если пользователь ввел в поле недопустимое значение, необходимо сообщить ему об этом и дать возможность исправить ошибку. Если ошибка очевидна, то можно ограничиться звуковым сигналом (заметим, что в ранних версиях .NET для этого не было встроенных методов, но их можно программировать). Привлечь внимание пользователя можно и по-другому, изменив цвет фона или текста элемента управления (при помощи его свойств BackColor и ForeColor соответственно). Для вывода более информативного описания ошибки можно воспользоваться методом MessageBox.Show, отображающим небольшое модальное окно с заданным сообщением: MessageBox.Show ("Вы ввели неверное значение " + aControl.Text); aControl.Focus (); Удобный способ оповещения пользователя об ошибках ввода предоставляет компонент ErrorProvider. Он позволяет для любого элемента управления задать текст сообщения об ошибке. Тогда рядом с данным элементом появляется предупреждающий значок, а если навести на этот значок указатель мыши, то отображается всплывающая подсказка с подготовленным сообщением. 7 Компонент ErrorProvider относится к провайдерам дополнительных свойств и расположен в секции Windows Forms панели Toolbox. Задать элементу сообщение об ошибке можно сразу в дизайнере посредством окна Properties либо программно (по мере необходимости, что более естественно) методом SetError компонента ErrorProvider. Его параметрами являются элемент управления (ссылка на него) и текст сообщения. Например: myErrorProvider.SetError (nameTextBox, "Имя не может быть пустым!"); Некоторые свойства компонента ErrorProvider определяют способ отображения сообщения об ошибке. Свойство Icon задает значок ошибки. Одна форма может содержать несколько экземпляров ErrorProvider (например, ошибки и предупреждения) с различными значками. Другое свойство этого компонента – BlinkStyle – заставляет значок мигать, частоту мигания определяет свойство BlinkRate. Резюме На уровне формы поля проверяются централизованно, на уровне поля – индивидуально по мере заполнения. У элемента TextBox есть свойства времени разработки, определяющие допустимые значения: MaxLength, PasswordChar, ReadOnly, MultiLine. Клавиатурные события (KeyDown, KeyUp, KeyPress) позволяют проверять символы, вводимые при нажатии клавиш и их сочетаний. Эти события генерируются текущим элементом управления. Если свойство KeyPreview формы установлено в true, то первой их генерирует форма. Для проверки вводимых данных обычно применяется событие Validating. Оно генерируется перед потерей фокуса элементом управления, если у элемента управления, который получает фокус следующим, свойство CausesValidation установлено в true. При помощи компонента ErrorProvider элементу можно назначить текст сообщения и значок для оповещения об ошибке ввода. Для отображения сообщений об ошибках во время выполнения применяется метод SetError. 8 5. Объектно-ориентированное программирование 5.1. Общие принципы C# наряду с ориентированным. другими Имеющиеся .NET-языками в языке является объектно- объектно-ориентированные возможности образуют его объектную модель. Представление сущностей реального мира посредством программных объектов называется абстрагированием (abstraction). Итак, программирование в .NET-окружении основано на объектах. Объект – это программная конструкция, содержащая набор логически связанных данных и операций. Объекты предоставляют некоторую функциональность другим компонентам, скрывая от них свое внутреннее устройство. Объекты создаются на основе шаблонов, называемых классами. Библиотека базовых классов .NET содержит набор готовых классов, которые можно применять для создания объектов в приложениях. Кроме того, среда Visual Studio позволяет разрабатывать новые классы на основе имеющихся. При создании объекта в памяти создается экземпляр соответствующего класса. Это можно сделать с помощью ключевого слова new: Widget myWidget = new Widget ( ); Объекты состоят из членов четырех видов: свойства, поля, методы и события. Поля и свойства содержат данные объекта, методы определяют действия, которые объект способен выполнять, а события представляют уведомления, которые объект может получать или отправлять другим объектам, если происходит нечто важное. Рассмотрим, например, объект Автомобиль. Его свойства и поля Цвет, Модель, Возраст, Расход_топлива представляют данные, описывающие состояние объекта. Методы Нажать_акселератор, Переключить_передачу или Повернуть_руль формируют поведение объекта. Событие представляет собой уведомление о важных происшествиях. Для объекта Автомобиль 9 необходимо, чтобы объект Двигатель уведомлял его о событии Перегрев, а взаимодействие с объектом Дерево должно вызвать событие Столкновение. Из примера следует, что сложные объекты способны содержать вложенные объекты и открывать к ним доступ, как к своим членам. Например, элемент управления TextBox поддерживает свойство Font, которое представляет собой объект типа Font. Аналогично любой экземпляр класса Form содержит и предоставляет набор Controls, в который входят все элементы управления, размещенные на форме. В основе программирования с использованием классов лежат три основных принципа: инкапсуляция, полиморфизм и наследование. Инкапсуляция состоит в объединении логически связанных данных и операций в общем контейнере, а также отделении (и сокрытии) реализации объекта от его интерфейса. Приложение взаимодействует с объектом через его интерфейс, состоящий из открытых свойств и методов. При неизменном интерфейсе оно сохраняет способность к взаимодействию с объектом, даже если в его новой версии реализация полностью изменится. Интерфейс не должен открывать доступ к внутренним данным объекта, поэтому поля с внутренними данными объекта редко объявляются с модификатором public. Благодаря полиморфизму, одни и те же открытые интерфейсы можно поразному реализовать в разных классах. Другими словами, полиморфизм позволяет вызывать методы и свойства объекта независимо от их реализации. Например, объект Водитель взаимодействует с объектом Автомобиль через открытый интерфейс. Если другой объект, например Грузовик или Спортивный_автомобилъ, поддерживает тот же открытый интерфейс, то объект Водитель сможет взаимодействовать и с ними, невзирая на различия в реализации интерфейса. Существуют два основных подхода к реализации полиморфизма: через интерфейсы и через наследование. Интерфейс – соглашение, определяющее поведение объекта. Интерфейс определяет список членов класса (свойств и методов), но ничего не говорит 10 об их реализации. В объекте допустимо реализовать несколько интерфейсов, а один и тот же интерфейс можно реализовать в разных классах. Наследование позволяет конструировать новые классы на основе имеющихся классов. Они называются производными (или дочерними) по отношению к базовым. В объектной модели .NET у каждого класса может быть только один непосредственный базовый класс. Производный класс получает тот же набор членов, к которому можно добавлять новые члены. Можно также изменить реализацию членов, унаследованную от базового класса, переопределив их. Представители производных классов могут взаимодействовать с другими объектами как экземпляры базового класса. Резюме Представление объектов реального мира программными конструкциями называется абстрагированием. Экземпляры классов воспроизводят характеристики объектов реального мира посредством их членов. Классы аналогичны «чертежам» объектов. При создании объекта в памяти создается копия данных, определенных в классе, и устанавливаются начальные значения для членов-переменных. Класс – это шаблон, на основе которого можно создать сколько угодно объектов этого класса. Инкапсуляция – один из фундаментальных принципов ООП. Объект должен содержать необходимые ему данные и код для манипулирования ими. Данные объекта не должны быть доступными другим объектам. Интерфейсы предоставляют доступ только к свойствам и методам. Полиморфизм позволяет по-разному реализовать один и тот же открытый интерфейс в разных объектах. В C# существуют два основных способа реализации полиморфизма: через интерфейсы и наследование.