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 – выбранное значение. В данном случае, как часто бывает, они одинаковы.

Привязка к данным

Некоторые элементы управления: списки, таблицы и другие - имеют свойство 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();
  }

Классы проверки данных (Валидаторы)

Данные из форм обычно записываются в базы данных, и тип информации в них должен соответствовать типу и длине данных в полях таблиц баз данных. Кроме того, иногда нужно вводить взаимосвязанные данные, например пароль во время регистрации нужно вводить 2 раза, и он в обоих полях должен совпадать. Некоторые хакеры пытаются вводить в формы программные коды, чтобы взломать вашу систему. Бдительность и еще раз бдительность, как говорил товарищ... не помню кто. А если пользователь ввел неверные данные случайно, сервер выдаст непонятное сообщение об ошибке, и ценный клиент уйдет с нашего сайта на другой.

Прежде чем работать с данными, нужно убедиться, что:

* В определенное поле введена информация;
* Текст в поле адрес имеет форму электронного адреса(с @ и с точкой);
* Дата рождения разумна, например, пользователь не сообщил о себе, что ему 300 лет или 1 годик.
* Пароль достаточно сложен и не совпадает с логином.

Проверка может происходить и на стороне клиента, и на сервере. При валидации на стороне клиента в страницу встраивается код на Javascript. Если данные в форме не проходят проверку, страница просто не будет отправлена на сервер. Таким образом избежим лишнего трафика и не будем загружать сервер. С другой стороны, валидации на стороне сервера более надежна. Javascript-код хакеры могут легко посмотреть и отправить неправильные данные, которые пройдут эту проверку. Наконец, Javascript можно просто выключить в настройках браузера. При валидации на стороне сервера данные проверяются программой на полноценном языке. Ее код пользователю неизвестен. В результате проверки генерируется новая страница с сообщениями об ошибках. Самая разумная стратегия – применять комбинацию этих методов. Предварительная проверка у клиента защитит от опечаток, а серьезная проверка на сервере – от злонамеренного взлома.

Существует целый ряд серверных элементов управления, которые не занимаются выводом информации, а проверяют данные, введенные пользователем. ASP.NET 2.0 сам определяет тип браузера и генерирует наиболее подходящий для данного случая код, который. Если браузер поддерживает Javascript-код, который он может послать, то валидация или ее часть происходит на стороне клиента. Если браузер не поддерживает Javascript, то вся валидация происходит на сервере.

Получить доступ к валидаторам просто – раскройте в Toolbox вкладку Validation.

Класы валидаторов образуют иерархию, во главе которой стоит абстрактный класс BaseValidator.

Базовый класс валидаторов сам наследник класса Label, так что по существу все валидаторы - метки, текст в которых становится видимым, когда не выполняются определеннные нами условия проверки. По умолчанию текст в валидаторах красный. (Вспомните школу и замечания учительнице в тетради). Но конечно же, этот цвет поменять на более приятный. Все валидаторы имеют свойство ControlToValidate. Оно задает тот контрол, данные в котором проверяются данными валидатором. Этот элемент должен находиться в одном контейнере с валидатором.
Общие свойства валидаторов
Display Предоставлять ли место статически или динамически
EnableClientScript Разрешать ли генерировать клиентский код
ErrorMessage Текст сообщения об ошибке
IsValid Прошел ли валидацию связанный с валидатором элемент
Инициация проверки данных

Проверка всегда инициируется каким-либо событием. Обычно это щелчок на кнопках 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">&nbsp;(Required)</span>&nbsp;
  <asp:textbox id="TextBox1" runat="server"></asp:textbox>         &nbsp;
  <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>

ValidationExpression – регулярное выражение, на соответствие которому проходит проверку значение текстового поля. в Visual Studio 2005 предоставляет несколько готовых шаблонов регулярных выражений, которые можно выбрать в окне свойств – телефонных номеров разных стран, адресов, и, самое полезное, шаблоны электронной почты и адреса в интернете.

С одним элементом управления может быть связано несколько валидаторов. Например, электронный адрес проверяется и на соответствие шаблону, и на обязательное заполнение.

Свойство Page.IsValid позволяет определить, прошла ли вся страница валидацию. Для браузеров, которые поддерживают DHTML, проверка происходит на стороне клиента. Для этого автоматически генерируется JavaScript-код. Таким образом экономятся ресурсы сервера и трафик, которые бы пришлось потратить на передачу неправильных данных.

Валидаторы сравнения

CompareValidator сравнивает значение со значением в другом элементе управления или с константой. Также можно проверить, можно ли конвертировать значение в связанном с ним элементе управления в какой-либо тип.

Свойство Operator позволяет установить операцию, посредством которой происходит сравнение: Equal, NotEqual, GreaterThan, GreaterThanEqual, LessThan, LessThanEqual. Значение DataTypeCheck означает проверку на соответствие типу.

Свойство Type может принимать значения String, integer, Date, Double и Currency.

Свойство ControlToCompare задает элемент управления, с которым происходит сравнение. ValueToCompare задает значение. Из этих двух свойств установленным может быть только одно.

RangeValidator проверяет соответствие введенного значения диапазону, заданному своствами MinValue и MaxValue.

<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>

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. Второй валидатор – RequiredFieldValidator для пароля. Третий – 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>Спасибо за регистрацию!";
          }
      }

Вся проверка этой страницы происходит у клиента. Можете даже остановить сервер, чтобы в этом убедиться. Только когда все данные введены правильно, происходит отправка формы на сервер.
CustomValidator

Если нужно сделать такую проверку, которую не получается осуществить с помощью стандартных валидаторов, в игру вступает CustomValidator. В классе CustomValidator можно написать любую функцию, которая будет проверять значения как на стороне сервера, так и у клиента. Классический пример – проверка числа на четность.

Напишем пользователький валидатор, который будет проверять пароль на длину – не меньше 5 символов.