CustomValidator
Если нужно сделать такую проверку, которую не получается осуществить с помощью стандартных валидаторов, в игру вступает Custom Validator. В классе CustomValidator можно написать любую функцию, которая будет проверять значения как на стороне сервера, так и у клиента. Классический пример — проверка числа на четность.
Напишем пользовательский валидатор, который будет проверять пароль на длину — не меньше 5 символов:
<head> <title>Регистрация нового пользователя</title> <script language="JavaScript"> function validatePassword(oSrc, args) { args.IsValid = (args.Value.length > 5); } </script>
</head> <asp:CustomValidator ID="CustomValidator1" runat="server" ControlToValidate="txtPassword1" ErrorMessage="Слишком короткий пароль" Display="Static" ClientValidationFunction="validatePassword" >*</asp:CustomValidator>
Проверка происходит на стороне клиента функцией на JavaScript validatePassword. Для этого надо в свойстве ClientValidationFunction записать имя функции. В сгенерированном коде к кнопке привязан такой код:
<input type="submit" name="Button1" value="Button" onclick="javascript:WebForm_DoPostBackWithOptions(new WebForm_PostBackOptions("Button1", "true", "false", "false"))" id="Button1" />
Известно, что если функция, связанная с onclick, вернет логическое значение false, форма не будет отправлена на сервер:
<asp:CustomValidator ID="CustomValidator1" runat="server" ControlToValidate="txtPassword1" ErrorMessage="Слишком короткий пароль" Display="Static" OnServerValidate="ServerValidate" >*</asp:CustomValidator><br />
Произведем такую же валидацию на сервере. Чтобы запустить проверку на сервере, используется свойство OnServerValidate. Функция, которая указана в ней, входит в класс страницы и должна быть написана на C#:
void ServerValidate(object source, ServerValidateEventArgs args) { string password = args.Value.ToString(); int len = password.Length; args.IsValid = (len >= 5); }
ServerValidate вызывается после события Page_Load, поэтому нельзя, как в лекции 3, выводить результаты в Page_Load.
Можно также одновременно проводить и клиентскую, и серверную валидацию:
<asp:CustomValidator ID="CustomValidator1" runat="server" ControlToValidate="txtPassword1" ErrorMessage="Слишком короткий пароль" Display="Static" OnServerValidate="ServerValidate" ClientValidationFunction="validatePassword" >*</asp:CustomValidator>
Чтобы отключить возможность генерации клиентского кода для всех валидаторов, можно написать
void Page_Load() { foreach (BaseValidator bv in Page.Validators) { bv.EnableClientScript = false; } }
Для отображения сообщения об ошибке можно использовать звуки и картинки. Для этого в свойство ErrorMessage нужно записать не текст, а соответствующие теги HTML, например
ErrorMessage=’<img src="error.gif">’
Группы валидации
Иногда бывает нужно иметь на странице несколько кнопок, и при нажатии на каждую вводится информация из логически взаимосвязанных групп элементов управления. Поэтому должны проверяться значения только из этой группы. У всех валидаторов и элементов управления, через которые возможен ввод информации, есть свойство ValidationGroup. Функцию Page.Validate() тоже можно использовать с таким параметром. Если происходит нажатие на кнопку с установленным ValidationGroup, запускается проверка тех валидаторов, у которых это свойство такое же.
Инициация проверки данных
Проверка всегда инициируется каким-либо событием. Обычно это щелчок на кнопках Button, ImageButton, LinkButton, в которые по умолчанию свойство CausesValidation установлено в True. Можно убрать это свойство для некоторых кнопок, которым оно не нужно, например, для кнопки Cancel.
В примере с автосалоном на странице имеются несколько валидаторов:
<asp:requiredfieldvalidator id="RequiredFieldValidator2" runat="server" ErrorMessage="Required" ControlToValidate="DropDownList1"> </asp:requiredfieldvalidator>
Класс RequiredFieldValidator проверяет, было ли изменено значение в связанном с ним элементе управления. Если, как в данном случае, это выпадающий список — первоначально выбрано пустое значение, но требуется, чтобы пользователь выбрал конкретную марку. Если выбор не был сделан, но кнопка submit была нажата, валидация проваливается и выводится текст, заданный в ErrorMessage или в Text. Валидаторы отображают текст, указанный в свойстве "Text", всегда, когда оно не пусто, а текст, установленный в свойстве "ErrorMessage" — тогда, когда свойство "Text" равно "". Первоначальное значение задается свойством InitialValue. Если это свойство не задано, то проверка проводится на отсутствие значения (например, пустой Textbox).
Для проверки корректности ввода электронного адреса используется класс RegularExpressionValidator:
<span class="label">Your Email</span><span class="label1">(Required)</span> <asp:textbox id="TextBox1" runat="server"></asp:textbox> <asp:RegularExpressionValidator id="RegularExpressionValidator1" runat="server" ControlToValidate="TextBox1" ErrorMessage="Not a valid Email" ValidationExpression="\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([- .]\w+)*"></asp:RegularExpressionValidator> <asp:RequiredFieldValidator id="RequiredFieldValidator1" runat="server" ControlToValidate="TextBox1" ErrorMessage="*"></asp:RequiredFieldValidator> </span>
Классы проверки данных (валидаторы)
Данные из форм обычно записываются в базы данных, и тип информации в них должен соответствовать типу и длине данных в полях таблиц баз данных. Кроме того, иногда нужно вводить взаимосвязанные данные, например, пароль во время регистрации нужно вводить 2 раза и он в обоих полях должен совпадать. Некоторые хакеры пытаются вводить в формы программные коды, чтобы взломать вашу систему. Бдительность и еще раз бдительность, как говорил товарищ... не помню кто. А если пользователь ввел неверные данные случайно, сервер выдаст непонятное сообщение об ошибке, и ценный клиент уйдет с нашего сайта на другой.
Прежде чем работать с данными, нужно убедиться, что:
в определенное поле введена информация;текст в поле "Адрес" имеет форму электронного адреса (с @ и с точкой);дата рождения разумна, например, пользователь не сообщил о себе, что ему 300 лет или 1 годик;пароль достаточно сложен и не совпадает с логином.
Проверка может происходить и на стороне клиента, и на сервере. При валидации на стороне клиента в страницу встраивается код на Javascript. Если данные в форме не проходят проверку, страница просто не будет отправлена на сервер. Таким образом мы избежим лишнего трафика и не будем загружать сервер. С другой стороны, валидация на стороне сервера более надежна. Javascript-код хакеры могут легко посмотреть и отправить неправильные данные, которые пройдут эту проверку. Наконец, Javascript можно просто выключить в настройках браузера. При валидации на стороне сервера данные проверяются программой на полноценном языке.
Ее код пользователю неизвестен. В результате проверки генерируется новая страница с сообщениями об ошибках. Самая разумная стратегия — применять комбинацию этих методов. Предварительная проверка у клиента защитит от опечаток, а серьезная проверка на сервере — от злонамеренного взлома.
Существует целый ряд серверных элементов управления, которые не занимаются выводом информации, а проверяют данные, введенные пользователем. ASP .NET 2.0 сам определяет тип браузера и генерирует наиболее подходящий для данного случая код. Если браузер поддерживает Javascript-код, который он может послать, то валидация или ее часть происходит на стороне клиента. Если браузер не поддерживает Javascript, то вся валидация происходит на сервере.
Получить доступ к валидаторам просто — раскройте в Toolbox вкладку "Validation".
Классы валидаторов образуют иерархию, во главе которой стоит абстрактный класс BaseValidator.
увеличить изображение
Рис. 5.1.
Базовый класс валидаторов сам наследник класса Label, так что по существу все валидаторы — метки, текст в которых становится видимым, когда не выполняются заданные нами условия проверки. По умолчанию текст в валидаторах — красный (вспомните школу и замечания учительницы в тетради). Но, конечно же, этот цвет можно поменять на более приятный. Все валидаторы имеют свойство ControlToValidate. Оно задает тот элемент управления, данные в котором проверяются данным валидатором. Этот элемент должен находиться в одном контейнере с валидатором.
Display | Предоставлять ли место статически или динамически |
EnableClientScript | Разрешать ли генерировать клиентский код |
ErrorMessage | Текст сообщения об ошибке |
IsValid | Прошел ли валидацию связанный с валидатором элемент управления |
Привязка к данным
Некоторые элементы управления: списки, таблицы и другие — имеют свойство DataSource, которое отвечает за привязку к данным. Тип этого свойства — object, то есть он может быть любым, но должен реализовывать интерфейс IEnumerable. Часто значениями этого свойства назначают коллекции. В таком случае нет нужды добавлять значения вручную. Свойство DataSource может быть привязано к коллекциям, поддерживающим интерфейсы IEnumerable, ICollection или IListSource. Источником данных также могут быть XML-файлы, базы данных. Вызовом метода DataBind данные привязываются к элементу управления. Метод Page.DataBind вызывает привязку данных у всех элементов на странице.
Приведенный ниже выпадающий список помогает выбрать континент для путешествия. Источник данных — динамический массив ArrayList. Используйте его, если в программе происходит много вставок и удалений:
void Page_Load() { ArrayList ContinentArrayList = new ArrayList(); ContinentArrayList.Add("Worldwide"); ContinentArrayList.Add("America"); ContinentArrayList.Add("Africa"); ContinentArrayList.Insert(1, "Asia-Pacific"); ContinentDropDownList.DataSource = ContinentArrayList; ContinentDropDownList.DataBind(); } //End Page_Load() .... <asp:DropDownList id="ContinentDropDownList" runat="server" />
Можно использовать в качестве источника данных хэш-таблицы (Hashtable). Хэш-таблицы — это структуры данных, которые были придуманы давно (см. том 3 "Искусства программирования" Д.
Кнута), но программисты долгое время были вынуждены реализовывать их вручную. В языке PHP обычный массив и есть хэш-таблица. В библиотеке STL для языка С++ тоже есть тип map, в котором данные хранятся таким способом. Хэш-таблицы позволяют очень быстро найти значение по ключу. Индекс в коллекции вычисляется как простая хэш-функция ключа. В C# ключи используются как индексаторы. Используйте Hashtable, если в программе часто осуществляется поиск. Вставка и удаление происходят в нем медленно. Ключи могут быть произвольного типа. В классе Object определен виртуальный метод GetHashCode, он и применяется в Hashtable:
<%@ Page Language="C#" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <script runat="server"> void calSelectChange(Object sender, EventArgs e) { lblShow.Visible = false; Hashtable hshDays = new Hashtable(); hshDays[Convert.ToDateTime("2/6/2006")] = "Экзамен по алгеб- ре"; hshDays[Convert.ToDateTime("3/6/2006")] = "Экзамен по С#"; hshDays[Convert.ToDateTime("4/6/2006")] = "Начало изучения курса ASP.NET"; hshDays[Convert.ToDateTime("1/6/2006")] = "День защиты де- тей"; DateTime datDateIn; datDateIn = calDays.SelectedDate; if (Page.IsPostBack) { lblShow.Text = "На этот день назначен: "; lblShow.Text += hshDays[datDateIn]; if (hshDays[datDateIn] == null) lblShow.Text = "Ничего не назначено"; lblShow.Visible = true; } } </script>
<html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Попробуем хэш-таблицу</title> </head> <body> <form id="form1" runat="server"> <div> <h3>Ежедневник </h3> Введите дату между 1/6/2006 и 30/6/2006 <asp:Calendar id="calDays" runat="server" OnSelectionChanged="calSelectChange" VisibleDate="06/06/2006" ></asp:Calendar> <br /> <br /> <asp:Label id="lblShow" runat="server"></asp:Label> </div> </form> </body> </html>
Здесь ключом хэш-таблицы является дата. Convert.ToDateTime конвертирует строку в тип даты. VisibleDate гарантирует, что на календаре будет июнь 2006 года. Если значений по ключу в таблице нет, то индексатор просто возвращает null. Значения можно вводить в любом порядке.
Хотелось бы добавить в страницу новую возможность введения новых записей. Можно ввести новые элементы управления — строку ввода и кнопку для подачи данных. При обработке нажатия на кнопку добавим в хэш-таблицу новое значение:
void Button1_Click(object sender, EventArgs e) { hshDays[calDays.SelectedDate]=TextBox1.Text; }
Эта страница не работает. Дело в том, что страница загружается заново, когда меняется дата. Хэш-таблица создается заново, и введенные в нее значения теряются. Как же решить эту проблему? Сделаем хэш-таблицу статической переменной:
static Hashtable hshDays; void calSelectChange(Object sender, EventArgs e) { DateTime datDateIn = calDays.SelectedDate;
lblShow.Text = " На этот день назначен: "; lblShow.Text += hshDays[datDateIn]; if (hshDays[datDateIn] == "") lblShow.Text = "Ничего не назначено"; } void Page_Init() { if (!Page.IsPostBack) { hshDays=new Hashtable(); hshDays[Convert.ToDateTime("2/6/2006")] = "Экзамен по ал- гебре"; hshDays[Convert.ToDateTime("3/6/2006")] = "Экзамен по С#"; hshDays[Convert.ToDateTime("4/6/2006")] = "Начало изуче- ния курса ASP.NET"; hshDays[Convert.ToDateTime("1/6/2006")] = "День защиты детей"; Session["Diary"]= hshDays; } }
void Record(Object sender, EventArgs e) { DateTime datDateIn = calDays.SelectedDate; hshDays[datDateIn]= Entrance.Text; lblShow.Text = hshDays[datDateIn].ToString(); }
Свойство AutoPostBack
Программирование в ASP .NET ориентировано на события. События на странице (например нажатие на кнопку) обрабатываются на сервере. Изменения в тексте поля редактирования, выбора опции в списке, нажатие на флажок или переключатель не вызывают немедленной отправки на сервер. Этого можно добиться, если установить свойство AutoPostBack для этих элементов.
Если AutoPostBack установлен для элемента управления TextBox, то для него будет вызываться событие TextChanged, как только поле потеряет фокус или будет нажата клавиша Enter. Чтобы это свойство работало, браузер должен поддерживать ECMAScript (стандарт JavaScript, принятый Европейской ассоциацией производителей компьютеров).
Источником данных для элементов управления могут служить таблицы данных. Давайте разберем пример, входящий в состав Visual Studio — CarSelectorSample. Действие происходит в электронном магазине автомобилей. Имеются разные марки машин, причем для каждой марки имеются несколько моделей. При выборе марки машины в первом списке во второй список автоматически грузятся соответствующие модели:
Brand | Buick | Chevrolet | Pontiac | Toyota | Mileage | Features |
Power seat | ||||||
Buick | Century | Impala | Grand Am | Avalon | 0-10000 | Leather seat |
Chevrolet | LeSabre | Malibu | Grand Prix | Camry | 10000-20000 | Sun roof |
Pontiac | Park Avenue | Metro | Montana | Camry Solara | 20000-30000 | CD player |
Toyota | Regal | Prizm | Sunfire | Celica | 30000 and more | ABS |
Все данные, используемые на этой странице, собраны в таблицу. Для хранения такой таблицы существует класс DataTable. Таблица состоит из столбцов — DataColumn и строк DataRow.
Класс DataView позволяет создавать различные представления данных таблицы. Первый столбец служит источником данных списка марок. В зависимости от выбранной модели, в список моделей загружается одна из 2-5 колонок.
Вначале создается таблица:
Cars = new DataTable(); Cars.Columns.Add(new DataColumn("Brand", typeof(string)));
Здесь вызывается один из конструкторов DataColumn. Первый аргумент — название колонки, второй — тип:
CarRow = Cars.NewRow();
Создается новая строка таблицы. Ячейка таблицы задается с помощью индекса строки:
CarRow[6]= "Power seat";
И строка добавляется в таблицу:
Cars.Rows.Add(CarRow);
У выпадающего списка марок установлено свойство AutoPostBack. Это значит, что страница автоматически подается на сервер, когда в этом списке меняется выбранный элемент.
В обработчике выбора нового элемента вначале выясняется, какой элемент выбран:
string selected = DropDownList1.SelectedItem.Value;
В операторе switch происходит переключение второго списка на один из столбцов таблицы заданием свойств DataTextField и DataValueField, где DataTextField — текст, отображаемый в списке, а DataValueField — выбранное значение. В данном случае, как часто бывает, они одинаковы.
ValidationSummary
Класс ValidationSummary позволяет вывести итоговую информацию по всем валидаторам на странице. Она может быть выведена в различной форме:
BulletList — список со значками;List — обычный список;SingleParagraph — простой параграф.
Информацию можно выводить на странице, а можно в информационном окне, если ShowMessage поставить в True. Для всех валидаторов выводится свойство Error Message, а не текст. Text выводится в самом валидаторе.
Вернемся к странице Registration.aspx. Добавим в него еще одно поле для ввода пароля:
<form runat="server" id="input"> <asp:Label ID="Label1" runat="server" Text="Введите имя:" Width="140px"></asp:Label> <asp:TextBox ID="txtName" runat="server" CausesValidation="True" /> <asp:Label ID="Label2" runat="server" Text="Введите адрес:" Width="140px"></asp:Label> <asp:TextBox id="txtAddress" runat="server" textmode="multiline" rows="5" /> <br/><br /> <asp:Label ID="Label3" runat="server" Text="Введите пароль:" Width="140px"></asp:Label> <asp:TextBox id="txtPassword" runat="server" textmode="password" /> <br /> <br /> <asp:Label ID="Label4" runat="server" Text="Повторите пароль" Width="140px"></asp:Label> <asp:TextBox id="TextBox1" runat="server" textmode="password" /><br /> <asp:Button ID="Button1" runat="server" Text="Submit" /> </form>
Свойство CausesValidation работает, когда элемент управления теряет фокус. В таком случае связанный с ним валидатор показывает значение своего свойства Text.
Нам нужно, чтобы имя было обязательно введено, а пароль совпадал в обоих текстовых полях.
Перетащите RequiredFieldValidator и бросьте его на форму. ControlToValidate установите в txtName. Второй валидатор — Required FieldValidator для пароля. Третий — CompareValidator, который сравнивает значение паролей:
<asp:RequiredFieldValidator ID="RequiredFieldValidator1" ControlToValidate="txtName" Display="Static" ErrorMessage="Имя необходимо ввести" runat="server" >*</asp:RequiredFieldValidator>
<asp:RequiredFieldValidator ID="RequiredFieldValidator2" runat="server" ErrorMessage="Пароль не должен быть пустым" ControlToValidate="txtPassword1">*</asp:RequiredFieldValidator>
<asp:CompareValidator ID="CompareValidator1" runat="server" ControlToValidate="txtPassword1" ErrorMessage="Пароли должны совпадать!" ControlToCompare="txtPassword2"></asp:CompareValidator><br />
А также один ValidationSummary:
<asp:ValidationSummary ID="ValidationSummary1" runat="server" />
Поставим кнопку, при нажатии на которую будет происходить проверка:
<asp:Button ID="Button1" runat="server" Text="Валидация" OnClick="Validate_Click" />
Обработчик нажатия на кнопку подтверждает ввод, если валидация прошла успешно:
protected void Validate_Click(object sender, EventArgs e) { if (Page.IsValid) { lblName.Text = ""; lblAddress.Text = ""; lblPassword.Text = ""; input.Visible = false; if (txtName.Text != "") lblName.Text = "Вы ввели имя: " + txtName.Text; if (txtAddress.Text != "") lblAddress.Text = "Вы ввели адрес: " + txtAddress.Text; if (txtPassword1.Text != "") lblPassword.Text = "Вы ввели пароль: " + txtPassword1.Text + "<br>Спасибо за регистрацию!"; } }
Вся проверка этой страницы происходит у клиента. Можете даже остановить сервер, чтобы в этом убедиться. Только когда все данные введены правильно, происходит отправка формы на сервер.
Валидаторы сравнения
CompareValidator сравнивает значение со значением в другом элементе управления или с константой. Также можно проверить, можно ли конвертировать значение в связанном с ним элементе управления в какой-либо тип.
Свойство Operator позволяет установить операцию, посредством которой происходит сравнение: Equal, NotEqual, GreaterThan, GreaterThanEqual, LessThan, LessThanEqual. Значение DataTypeCheck означает проверку на соответствие типу.
Свойство Type может принимать значения String, Integer, Date, Double и Currency.
Свойство ControlToCompare задает элемент управления, с которым происходит сравнение. ValueToCompare задает значение. Из этих двух свойств установленным может быть только одно.
RangeValidator проверяет соответствие введенного значения диапазону, заданному свойствами MinimumValue и MaximumValue:
<asp:TextBox ID="TextBox1" runat="server"></asp:TextBox> <asp:RangeValidator ID="RangeValidator1" runat="server" ControlToValidate="TextBox1" ErrorMessage="Не больше 10 бутылок пива в одни руки" MaximumValue="10" MinimumValue="1" Type="Integer"> </asp:RangeValidator>
в том, чтобы получать данные
Назначение форм — в том, чтобы получать данные от пользователей, но данные не имеют смысла, если они введены неправильно. Элементы-валидаторы позволяют автоматизировать рутинные действия по проверке данных и гарантировать ввод данных, соответствующих нашим требованиям.