8. Серверні компоненти ASP — 2
8.1. Introduction
Ми продовжуємо роботу над користувацьким інтерфейсом, розглядаючи:
- компоненти перевірки даних
- спосіб прив’язування даних до серверних компонентів
- серверні компоненти HTML
8.2. Компоненти перевірки даних
8.2.1. Вступ
У додатках з формами надзвичайно важливо перевіряти правильність даних, що вводяться в них. У прикладі з розрахунком податку ми мали таку форму:

Після отримання значень з цієї форми нам потрібно перевірити правильність введених даних: кількість дітей та заробітна плата мають бути додатними цілими числами або нулем. Якщо це не так, форма повертається клієнту в тому вигляді, в якому вона була заповнена, з повідомленнями про помилки. Ми обробляли цей випадок за допомогою двох представлень: одного для наведеної вище форми, а іншого — для повідомлень про помилки:

ASP.NET пропонує компоненти, так звані компоненти перевірки, які дозволяють перевіряти такі випадки:
Компонент | Роль |
перевіряє, чи поле не є порожнім | |
порівнює два значення між собою | |
перевіряє, чи значення знаходиться в межах двох меж | |
перевіряє, чи поле відповідає регулярному виразу | |
дозволяє розробнику задавати власні правила перевірки — цей компонент може замінити всі інші | |
дозволяє зібрати повідомлення про помилки, видані попередніми елементами контролю, в одному місці на сторінці |
Тепер ми розглянемо кожен із цих компонентів.
8.2.2. RequiredFieldValidator
Ми створюємо таку сторінку [requiredfieldvalidator1.aspx]:
![]() |
№ | назва | тип | властивості | роль |
1 | TextBox | EnableViewState=true | поле введення | |
2 | RequiredFieldValidator | EnableViewState=false EnableClientScript=true ErrorMessage=Поле [ім'я] є обов'язковим | компонент перевірки | |
3 | Button | EnableViewState=false CausesValidation=true | кнопка [submit] |
Поле [RequiredFieldValidator1] використовується для відображення повідомлення про помилку, якщо поле [txtNom] порожнє або містить послідовність пробілів. Його властивості такі:
поле, значення якого має перевірятися компонентом. Що означає значення компонента? Це значення атрибута [value] відповідного тегу HTML. Для [TextBox] це буде вміст поля введення, для [DropDownList] — значення вибраного елемента. | |
логічне значення — при значенні «true» вказує, що вміст попереднього поля має перевірятися також на стороні клієнта. У цьому випадку браузер відправить форму лише за умови, що вона не містить помилок. Проте перевірки валідації виконуються також на сервері на випадок, якщо клієнт не є, наприклад, браузером. | |
повідомлення про помилку, яке компонент повинен відобразити у разі виявлення помилки |
Перевірка даних сторінки за допомогою елементів валідації виконується лише в тому випадку, якщо кнопка або посилання, що викликало [POST] на сторінці, має властивість [CausesValidation=true]. [true] — це значення за замовчуванням для цієї властивості.
Запустимо цю програму. Спочатку скористаємося браузером [Mozilla]:

Код HTML, отриманий [Mozilla], має такий вигляд:
<html>
<head>
</head>
<body>
<form name="_ctl0" method="post" action="requiredfieldvalidator1.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwxNDI1MDc1NTU1Ozs+SGtdZvVxefDCDxnsqbDnqCaROsk=" />
<p>
Demande du dossier de candidature au DESS
</p>
<fieldset>
<legend>[--Identité--]</legend>
<table>
<tbody>
<tr>
<td>
Nom*</td>
<td>
<input name="txtNom" type="text" id="txtNom" />
</td>
</tr>
</tbody>
</table>
</fieldset>
<p>
<input type="submit" name="btnEnvoyer" value="Envoyer" onclick="if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate(); " language="javascript" id="btnEnvoyer" />
</p>
</form>
</body>
</html>
Видно, що кнопка [btnEnvoyer] пов'язана з функцією JavaScript через свій атрибут [onclick]. Також можна помітити, що сторінка не містить жодного коду [Javascript]. Перевірка [typeof(Page_ClientValidate) == 'function'] завершиться невдало, і функція JavaScript не буде викликана. Тоді форма буде відправлена на сервер, і саме він здійснить перевірку правильності даних. Скористаймося кнопкою [Envoyer], не вказуючи імені. Відповідь сервера така:

Що сталося? Форма була відправлена на сервер. Сервер виконав код усіх компонентів перевірки, що знаходяться на сторінці. Тут є лише один такий компонент. Якщо хоча б один компонент перевірки виявляє помилку, сервер повертає форму в тому вигляді, в якому вона була заповнена (фактично, для компонентів із атрибутом [EnableViewState=true], а також із повідомленням про помилку для кожного компонента перевірки, що виявив помилку). Це повідомлення є атрибутом [ErrorMessage] компонента перевірки. Саме цей механізм ми бачимо в дії вище. Якщо ми введемо щось у поле [nom], повідомлення про помилку більше не з’являтиметься під час перевірки форми:

Тепер скористаємося Internet Explorer і відкриємо URL-адресу [http://localhost/requiredfieldvalidator1.aspx]. Ми отримаємо таку сторінку:

Код HTML, отриманий за допомогою IE, виглядає так:
<html>
<head>
</head>
<body>
<form name="_ctl0" method="post" action="requiredfieldvalidator1.aspx" language="javascript" onsubmit="ValidatorOnSubmit();" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDwxNDI1MDc1NTU1Ozs+SGtdZvVxefDCDxnsqbDnqCaROsk=" />
<script language="javascript" src="/aspnet_client/system_web/1_1_4322/WebUIValidation.js"></script>
<p>
Demande du dossier de candidature au DESS
</p>
<fieldset>
<legend>[--Identité--]</legend>
<table>
<tbody>
<tr>
<td>
Nom*</td>
<td>
<input name="txtNom" type="text" id="txtNom" />
<span id="RequiredFieldValidator1" controltovalidate="txtNom" errormessage="Le champ [nom] est obligatoire" evaluationfunction="RequiredFieldValidatorEvaluateIsValid" initialvalue="" style="color:Red;visibility:hidden;">Le champ [nom] est obligatoire</span>
</td>
</tr>
</tbody>
</table>
</fieldset>
<p>
<input type="submit" name="btnEnvoyer" value="Envoyer" onclick="if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate(); " language="javascript" id="btnEnvoyer" />
</p>
<script language="javascript">
<!--
var Page_Validators = new Array(document.all["RequiredFieldValidator1"]);
// -->
</script>
<script language="javascript">
<!--
var Page_ValidationActive = false;
if (typeof(clientInformation) != "undefined" && clientInformation.appName.indexOf("Explorer") != -1) {
if (typeof(Page_ValidationVer) == "undefined")
alert("Impossible de trouver la bibliothèque de scripts /aspnet_client/system_web/1_1_4322/WebUIValidation.js. Essayez de placer ce fichier manuellement ou effectuez une réinstallation en exécutant 'aspnet_regiis -c'.");
else if (Page_ValidationVer != "125")
alert("Cette page utilise une version incorrecte de WebUIValidation.js. La page requiert la version 125. La bibliothèque de scripts est " + Page_ValidationVer + ".");
else
ValidatorOnLoad();
}
function ValidatorOnSubmit() {
if (Page_ValidationActive) {
ValidatorCommonOnSubmit();
}
}
// -->
</script>
</form>
</body>
</html>
Видно, що цей код набагато більший за той, який отримав [Mozilla]. Ми не будемо вдаватися в подробиці. Різниця полягає в тому, що сервер включив функції JavaScript у надісланий документ HTML. Чому отримано два різних коди HTML, хоча URL-адреса, яку запитували обидва браузери, однакова? Ми вже зазначали, що технологія ASP.NET змушує сервер адаптувати документ HTML, надісланий клієнту, відповідно до можливостей цього клієнта. На ринку існують різні браузери, які не всі мають однакові можливості. Наприклад, браузери Microsoft і Netscape використовують різні об’єктні моделі для документів, які вони отримують. Тому код JavaScript для обробки цього документа на стороні браузера має відмінності між цими двома браузерами. Крім того, розробники створюють нові версії своїх браузерів, постійно вдосконалюючи їхні можливості. Тому документ HTML, який розуміє IE5, може бути нерозумілим для IE3. Вище наведено приклад такої адаптації сервера до можливостей свого клієнта. З причини, яка тут не розглядалася детально, веб-сервер визначив, що клієнт [Mozilla] не мав можливості обробляти код JavaScript для перевірки. Тому цей код не був включений у документ HTML, який йому було надіслано, і перевірка відбулася на стороні сервера. Що стосується [IE6], цей код JavaScript, як ми бачимо, був включений у надісланий документ HTML. Щоб побачити, як це працює, проведемо такий експеримент:
- зупинімо веб-сервер
- скористаємося кнопкою [Envoyer], не заповнюючи поле [nom]
Ми отримаємо таку нову сторінку:

Саме браузер вивів повідомлення про помилку. Адже сервер зупинено. У цьому можна переконатися, ввівши щось у поле [nom] і натиснувши кнопку підтвердження. Цього разу відповідь така:

Що сталося? Браузер виконав код JavaScript для перевірки. Цей код визнав, що сторінка є валідною. Тому браузер відправив форму на веб-сервер, який був вимкнений. Браузер це виявив і відобразив наведену вище сторінку. Якщо перезапустити сервер, все повернеться до норми.
Що можна винести з цього прикладу?
- важливість поняття «компонент перевірки», який дозволяє повернути клієнту будь-яку неправильно заповнену форму
- важливість параметра [VIEWSTATE], який дозволяє повернути цю форму саме в тому вигляді, в якому вона була заповнена
- здатність сервера адаптуватися до свого клієнта. Клієнт ідентифікується за заголовком HTTP [User-Agent:], який він надсилає серверу. Отже, це не сервер «вгадує», з ким він має справу. Ця здатність до адаптації є дуже важливою для розробника, якому не доводиться турбуватися про тип клієнта свого додатка.
8.2.3. CompareValidator
Ми створюємо таку сторінку [comparevalidator1.aspx]:

№ | назва | тип | властивості | роль |
1 | DropDownList | EnableViewState=true | випадаючий список | |
2 | DropDownList | EnableViewState=true | випадаючий список | |
3 | CompareValidator | EnableViewState=false EnableClientScript=true ErrorMessage=Неправильний вибір 1 Значення для порівняння=? Operator=NotEqual Тип=рядок | компонент перевірки | |
4 | CompareValidator | EnableViewState=false EnableClientScript=true ErrorMessage=Неправильний вибір 2 ControlToCompare=? Operator=Не дорівнює Тип=рядок | компонент перевірки | |
5 | Button | EnableViewState=false CausesValidation=true | кнопка [submit] |
Компонент [CompareValidator] призначений для порівняння двох значень між собою. Можна використовувати оператори [Equal, NotEqual, LessThan, LessThanEqual, GreaterThan, GreaterThanEqual]. Перше значення задається властивістю [ControlToValidate], друге — властивістю [ValueToCompare], якщо попереднє значення потрібно порівняти з константою, або властивістю [ControlToCompare], якщо його потрібно порівняти зі значенням іншого компонента. Важливі властивості компонента [CompareValidator] такі:
поле, вміст якого має перевірятися компонентом | |
логічне значення — значення «true» вказує, що вміст попереднього поля також має перевірятися на стороні клієнта | |
повідомлення про помилку, яке компонент повинен відобразити у разі виявлення помилки | |
значення, з яким має порівнюватися значення поля [ControlToValidate] | |
компонент, значення якого має бути порівняно зі значенням поля [ControlToValidate] | |
оператор порівняння між двома значеннями | |
тип значень, що порівнюються |
Код представлення цієї сторінки такий:
<html>
<head>
</head>
<body>
<form runat="server">
<p>
Demande du dossier de candidature au DESS
</p>
<fieldset>
<legend>[--Options du DESS pour lesquelles vous candidatez--]</legend>
<table>
<tbody>
<tr>
<td>
Option1*</td>
<td>
Option2</td>
</tr>
<tr>
<td>
<asp:DropDownList id="cmbChoix1" runat="server">
<asp:ListItem Value="?" Selected="True">?</asp:ListItem>
<asp:ListItem Value="Automatique">Automatique</asp:ListItem>
<asp:ListItem Value="Informatique">Informatique</asp:ListItem>
</asp:DropDownList>
</td>
<td>
<asp:DropDownList id="cmbChoix2" runat="server">
<asp:ListItem Value="?">?</asp:ListItem>
<asp:ListItem Value="Automatique">Automatique</asp:ListItem>
<asp:ListItem Value="Informatique">Informatique</asp:ListItem>
</asp:DropDownList>
</td>
</tr>
<tr>
<td>
<asp:CompareValidator id="CompareValidator1" runat="server" ErrorMessage="Choix 1 invalide" ControlToValidate="cmbChoix1" ValueToCompare="?" Operator="NotEqual"></asp:CompareValidator>
</td>
<td>
<asp:CompareValidator id="CompareValidator2" runat="server" ErrorMessage="Choix 2 invalide" ControlToValidate="cmbChoix2" Operator="NotEqual" ControlToCompare="cmbChoix1"></asp:CompareValidator>
</td>
</tr>
</tbody>
</table>
</fieldset>
<p>
<asp:Button id="btnEnvoyer" runat="server" Text="Envoyer"></asp:Button>
</p>
</form>
</body>
</html>
Вимоги до перевірки сторінки такі:
- значення, вибране в [cmbChoix1], має відрізнятися від рядка «?». Це передбачає такі властивості для компонента [CompareValidator1]: Operator=NotEqual, ValueToCompare=?, Type=string
- значення, вибране в [cmbChoix2], має відрізнятися від значення, вибраного в [cmbChoix1]. Це передбачає такі властивості для компонента [CompareValidator2]: Operator=NotEqual, ControlToCompare=cmbChoix1, Type=string
Ми запускаємо цю програму. Ось приклад сторінки, отриманої під час обміну даними між клієнтом і сервером:

Якщо цю ж сторінку відкриває Internet Explorer 6, у неї вбудовується код JavaScript, що призводить до дещо іншої роботи. Про можливі помилки повідомляється, щойно користувач змінює значення в одному з випадаючих списків. Це покращує зручність користування.
8.2.4. CustomValidator, RangeValidator
Ми створюємо таку сторінку [customvalidator1.aspx]:

№ | назва | тип | властивості | роль |
DropDownList | EnableViewState=true | випадаючий список | ||
CompareValidator | EnableViewState=false EnableClientScript=true ErrorMessage=Недійсний диплом Operator=NotEqual ValueToCompare=? Type=String | компонент перевірки елемента управління [1] | ||
TextBox | EnableViewState=false | поле введення | ||
CustomValidator | EnableViewState=false EnableClientScript=true ErrorMessage=Неправильна точність диплома ClientValidationFunction=chkAutreDiplome | компонент перевірки поля [3] | ||
TextBox | EnableViewState=false | поле введення | ||
RangeValidator | EnableViewState=false EnableClientScript=true ErrorMessage=Рік отримання диплома повинен бути в діапазоні [1990,2004] MinValue=1990 MaxValue=2004 Тип=Ціле число ControlToValidate=txtAnDiplome | компонент перевірки поля [5] | ||
RequiredFieldValidator | EnableViewState=false EnableClientScript=true ErrorMessage=Необхідно вказати рік отримання диплома ControlToValidate=txtAnDiplome | компонент перевірки поля [5] | ||
Button | EnableViewState=false CausesValidation=true | кнопка [submit] |
Поле [RangeValidator] використовується для перевірки того, чи значення елемента управління знаходиться в межах двох меж [MinValue] та [MaxValue]. Його властивості такі:
поле, значення якого має перевірятися компонентом | |
логічне значення — якщо «true», вказує, що вміст попереднього поля також має перевірятися на стороні клієнта | |
повідомлення про помилку, яке компонент повинен відобразити у разі виявлення помилки | |
мінімальне значення поля, яке потрібно перевірити | |
максимальне значення поля, що перевіряється | |
тип значення поля, що підлягає перевірці |
Поле [CustomValidator] дозволяє виконувати перевірки, які не можуть виконувати компоненти перевірки, запропоновані ASP.NET. Ця перевірка здійснюється за допомогою функції, написаної розробником. Вона виконується на стороні сервера. Оскільки перевірка може здійснюватися також на стороні клієнта, розробнику може знадобитися розробити функцію JavaScript, яку він включить у документ HTML. Її властивості такі:
поле, значення якого має перевірятися компонентом | |
логічне значення — якщо «true», вказує, що вміст попереднього поля також має перевірятися на стороні клієнта | |
повідомлення про помилку, яке компонент повинен відобразити у разі виявлення помилки | |
функція, яку потрібно виконати на стороні клієнта |
Код для відображення сторінки такий:
<%@ Page Language="VB" %>
<script runat="server">
...
</script>
<html>
<head>
</head>
<body>
<form runat="server">
<p align="left">
Demande du dossier de candidature au DESS
</p>
<fieldset>
<legend>[--Votre dernier diplôme--]</legend>
<table>
<tbody>
<tr>
<td>
Diplôme*</td>
<td>
<asp:DropDownList id="cmbDiplomes" runat="server">
<asp:ListItem Value="?">?</asp:ListItem>
<asp:ListItem Value="[Autre]">[Autre]</asp:ListItem>
<asp:ListItem Value="Maîtrise">Maîперевірка</asp:ListItem>
<asp:ListItem Value="DESS">DESS</asp:ListItem>
<asp:ListItem Value="DEA">DEA</asp:ListItem>
</asp:DropDownList>
</td>
<td>
Si [Autre], précisez</td>
<td>
<asp:TextBox id="txtAutreDiplome" runat="server" EnableViewState="False"></asp:TextBox>
</td>
</tr>
<tr>
<td>
</td>
<td>
<p>
<asp:CompareValidator id="CompareValidator2" runat="server" ErrorMessage="Diplôme invalide" ControlToValidate="cmbDiplomes" ValueToCompare="?" Operator="NotEqual" ></asp:CompareValidator>
</p>
</td>
<td>
</td>
<td>
<asp:CustomValidator id="CustomValidator1" runat="server" ErrorMessage="Précision diplôme invalide" OnServerValidate="CustomValidator1_ServerValidate_1" EnableViewState="False" ClientValidationFunction="chckAutreDiplome"></asp:CustomValidator>
</td>
</tr>
<tr>
<td>
Année d'obtention*</td>
<td colspan="3">
<asp:TextBox id="txtAnDiplome" runat="server" Columns="4"></asp:TextBox>
<asp:RangeValidator id="RangeValidator1" runat="server" ErrorMessage="Année diplôme doit être dans l'intervalle [1990,2004]" ControlToValidate="txtAnDiplome" MinimumValue="1990" MaximumValue="2004" Type="Integer"></asp:RangeValidator>
<asp:RequiredFieldValidator id="RequiredFieldValidator2" runat="server" ErrorMessage="Année diplôme requise" ControlToValidate="txtAnDiplome"></asp:RequiredFieldValidator>
</td>
</tr>
</tbody>
</table>
</fieldset>
<p>
<asp:Button id="btnEnvoyer" runat="server" Text="Envoyer"></asp:Button>
</p>
</form>
</body>
</html>
Атрибут [OnServerValidate] компонента [CustomValidator] дозволяє вказати функцію, відповідальну за перевірку на стороні сервера. У наведеному вище прикладі компонент [CustomValidator1] викликає наступну процедуру [CustomValidator1_ServerValidate_1]:
<%@ Page Language="VB" %>
<script runat="server">
Sub CustomValidator1_ServerValidate_1(sender As Object, e As ServerValidateEventArgs)
' поле [txtAutreDiplome] має бути заповненим, якщо [cmbDiplomes]=[autre]
e.isvalid=not (cmbDiplomes.selecteditem.text="[Autre]" and txtAutreDiplome.text.trim="") _
and not (cmbDiplomes.selecteditem.text<>"[Autre]" and cmbDiplomes.selecteditem.text<>"?" and txtAutreDiplome.text.trim<>"")
End Sub
</script>
....
Функція перевірки, пов'язана з компонентом [CustomValidator], отримує два параметри:
- sender — об’єкт, що викликав подію
- e: подія. Процедура повинна встановити атрибут [e.IsValid] у значення «true», якщо перевірені дані є правильними, і «false» в іншому випадку.
У цьому випадку виконуються такі перевірки:
- випадаючий список [cmbDiplomes] не може мати значення [?]. Це перевіряється компонентом [CompareValidator2]
- користувач вибирає диплом зі списку, що розгортається [cmbDiplomes]. Якщо його диплома немає у списку, він має можливість вибрати опцію [Autre] зі списку. Тоді він повинен вказати свій диплом у полі введення [txtAutreDiplome]. Якщо в [cmbDiplomes] вибрано [Autre], то поле [txtAutreDiplome] має бути заповнене. Якщо в полі [cmbDiplomes] не вибрано ані [Autre], ані [?], то поле [txtAutreDiplome] має бути порожнім. Це перевіряється функцією, пов’язаною з компонентом [CustomValidator1].
- рік отримання диплома має знаходитися в діапазоні [1900-2004]. Це перевіряється за допомогою [RangeValidator1]. Однак якщо користувач не вводить нічого в поле, функція перевірки [RangeValidator1] не використовується. Тому додається компонент [RequiredFieldValidator2] для перевірки наявності вмісту. Це загальне правило. Вміст поля не перевіряється, якщо воно порожнє. Єдиний випадок, коли перевірка відбувається, — це коли поле пов’язане з компонентом [RequiredFieldValidator].
Ось приклад виконання в браузері [Mozilla]:

Якщо ми використовуємо браузер [IE6], ми можемо додати функцію перевірки на стороні клієнта для компонента [CustomValidator1]. Для цього цей компонент має такі властивості:
- EnableClientScript=true
- ClientValidationFunction=chkAutreDiplome
Функція [chkAutreDiplome ] має такий вигляд:
<head>
<meta http-equiv="pragma" content="no-cache" />
<script language="javascript">
function chkAutreDiplome(source,args){
// перевіряє правильність поля txtAutreDiplome
with(document.frmCandidature){
diplome=cmbDiplomes.options[cmbDiplomes.selectedIndex].text;
args.IsValid= !(diplome=="[Autre]" && txtAutreDiplome.value=="")
&& ! (diplome!="[Autre]" && diplome!="?" && txtAutreDiplome.value!="");
}
}
</script>
</head>
Функція [chkAutreDiplome] приймає ті самі два параметри, що й серверна функція [CustomValidator1_ServerValidate_1]:
- source — об’єкт, що спричинив подію
- args: подія. Вона має атрибут [IsValid], який функція повинна встановити в значення «true», якщо перевірені дані є правильними. Значення «false» призведе до відображення у місці компонента перевірки відповідного повідомлення про помилку, і форма не буде відправлена на сервер.
8.2.5. RegularExpressionValidator
Ми створюємо таку сторінку [regularexpressionvalidator1.aspx]:

№ | назва | тип | властивості | роль |
TextBox | EnableViewState=true | поле введення | ||
RequiredFieldValidator | ControlToValidate=txtMel Display=Dynamic | компонент перевірки [1] | ||
RegularExpressionValidator | ControlToValidate=txtMel Display=Dynamic | компонент перевірки елемента управління [1] | ||
Button | EnableViewState=false CausesValidation=true | кнопка [submit] |
Тут ми хочемо перевірити формат електронної адреси. Ми робимо це за допомогою компонента [RegularExpressionValidator], який дозволяє перевірити правильність поля за допомогою регулярного виразу. Нагадаємо, що регулярний вираз — це шаблон символьного рядка. Отже, ми перевіряємо вміст поля на відповідність шаблону. Оскільки вміст поля не перевіряється, якщо воно порожнє, нам також потрібен компонент [RequiredFieldValidator], щоб перевірити, чи електронна адреса не порожня. Клас [RegularExpressionValidator] має властивості, аналогічні до класів компонентів, які ми вже розглядали:
поле, значення якого має перевірятися компонентом | |
булеве значення — якщо «true», вказує, що вміст попереднього поля також має перевірятися на стороні клієнта | |
повідомлення про помилку, яке компонент повинен відобразити у разі виявлення помилки | |
регулярний вираз, з яким буде порівнюватися вміст поля [ControlToValidate] | |
режим відображення: Static — поле завжди присутнє, навіть якщо воно не відображає повідомлення про помилку; Dynamic — поле присутнє лише за наявності повідомлення про помилку; None — повідомлення про помилку не відображається. Це поле існує також для інших компонентів, але дотепер не використовувалося. |
Регулярний вираз, що описує формат електронної адреси, може мати такий вигляд: \s*[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+\s*
Електронна адреса має вигляд [champ1.champ2....@champA.champB...]. Перед знаком @ обов’язково має бути принаймні одне поле, а після нього — принаймні два. Ці поля складаються з алфавітно-цифрових символів та знака -. Алфавітно-цифровий символ позначається символом \w. Послідовність [\w-] означає символ \w або символ -. Знак + після послідовності S означає, що вона може повторюватися 1 або більше разів, знак * означає, що вона може повторюватися 0 або більше разів, а знак ? означає, що вона може повторюватися 0 або 1 раз.
Одне поле електронної адреси відповідає шаблону [\w-]+. Те, що перед знаком @ обов’язково має бути принаймні одне поле, а після нього — принаймні два, розділені знаком ., відповідає шаблону: [\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+. Користувачеві можна дозволити вводити пробіли перед та після адреси. Вони будуть видалені під час обробки. Послідовність пробілів, яка може бути порожньою, позначається шаблоном \s*. Звідси випливає регулярний вираз для електронної адреси: \s*[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+\s*.
Тоді код для відображення сторінки виглядає так:
<html>
<head>
</head>
<body>
<form runat="server">
<p align="left">
Demande du dossier de candidature au DESS
</p>
<fieldset>
<legend>[--Votre adresse électronique où sera envoyé le dossier de candidature--]</legend>
<table>
<tbody>
<tr>
<td>
<asp:TextBox id="txtMel" runat="server" Columns="60"></asp:TextBox>
</td>
</tr>
<tr>
<td>
<p>
<asp:RequiredFieldValidator id="RequiredFieldValidator3" runat="server" Display="Dynamic" ControlToValidate="txtMel" ErrorMessage="Votre adresse électronique est requise"></asp:RequiredFieldValidator>
</p>
<p>
<asp:RegularExpressionValidator id="RegularExpressionValidator1" runat="server" Display="Dynamic" ControlToValidate="txtMel" ErrorMessage="Votre adresse électronique n'a pas un format valide" ValidationExpression="\s*[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+\s*"></asp:RegularExpressionValidator>
</p>
</td>
</tr>
</tbody>
</table>
</fieldset>
<p>
<asp:Button id="btnEnvoyer" runat="server" Text="Envoyer"></asp:Button>
</p>
</form>
</body>
</html>
8.2.6. ValidationSummary
З естетичних міркувань може виникнути потреба об'єднати повідомлення про помилки в одному місці, як у наведеному нижче прикладі [summaryvalidator1.aspx], де ми зібрали на одній сторінці всі попередні приклади:

Усі елементи перевірки мають такі властивості:
- [Display=Dynamic], завдяки чому елементи перевірки не займають місця на сторінці, якщо їхнє повідомлення про помилку порожнє
- [EnableClientScript=false], щоб заборонити будь-яку перевірку на стороні клієнта
- [Text=*]. Це буде повідомлення, що відображатиметься у разі помилки, тоді як вміст атрибута [ErrorMessage] відображатиметься елементом управління [ValidationSummary], який ми наводимо нижче.
Цей набір елементів управління розміщений у компоненті [Panel], який називається [vueFormulaire]. В іншому компоненті [Panel], який називається [vueErreurs], ми розміщуємо елемент управління [ValidationSummary]:

№ | назва | тип | властивості | роль |
1 | ValidationSummary | HeaderText=Сталися такі помилки, EnableClientScript=false, ShowSummary=true | відображає помилки всіх елементів перевірки на сторінці | |
2 | LinkButton | CausesValidation=false | посилання для повернення до форми |
Для посилання [lnkErreursToFormulaire] немає необхідності активувати перевірку, оскільки це посилання не надсилає жодних значень для перевірки.
Коли всі дані є правильними, користувачеві відображається такий вигляд [infos]:

1
№ | ім’я | тип | властивості | роль |
1 | Мітка | інформаційне повідомлення для користувача |
Код форматування цієї сторінки такий:
<html>
<head>
</head>
<body>
<form runat="server">
<p align="left">
Demande du dossier de candidature au DESS
</p>
<p>
<hr />
<asp:panel id="vueErreurs" runat="server">
<p align="left">
<asp:ValidationSummary id="ValidationSummary1" runat="server" ShowMessageBox="True" BorderColor="#C04000" BorderWidth="1px" BackColor="#FFFFC0" HeaderText="Les erreurs suivantes se sont produites"></asp:ValidationSummary>
</p>
<p>
<asp:LinkButton id="lnkErreursToFormulaire" onclick="lnkErreursToFormulaire_Click" runat="server" CausesValidation="False">Retour au formulaire</asp:LinkButton>
</p>
</asp:panel>
<asp:panel id="vueFormulaire" runat="server">
....
<asp:Button id="btnEnvoyer" onclick="btnEnvoyer_Click" runat="server" Text="Envoyer"></asp:Button>
</p>
</asp:panel>
<asp:panel id="vueInfos" runat="server">
<asp:Label id="lblInfo" runat="server"></asp:Label>
</asp:panel>
</form>
</body>
</html>
Ось кілька прикладів отриманих результатів. Ми перевіряємо вигляд [formulaire], не вводячи жодних значень:

Кнопка [Envoyer] дає нам таку відповідь:

Було відображено перегляд [erreurs]. Якщо скористатися посиланням для повернення до форми, ми побачимо її в такому стані:

Це вигляд [formulaire]. Зверніть увагу на символ [*] поруч із помилковими даними. Це поле [Text] з елементів перевірки, яке було відображено. Якщо ми правильно заповнимо поля, отримаємо вигляд [infos]:

Код управління сторінкою такий:
<%@ Page Language="VB" %>
<script runat="server">
' процедура, що виконується під час завантаження сторінки
Sub page_Load(sender As Object, e As EventArgs)
' при першому запиті відображається представлення [formulaire]
if not ispostback then
afficheVues(true,false,false)
end if
end sub
sub afficheVues(byval formulaireVisible as boolean, _
erreursVisible as boolean, infosVisible as boolean)
' набір переглядів
vueFormulaire.visible=formulaireVisible
vueErreurs.visible=erreursVisible
vueInfos.visible=infosVisible
end sub
Sub CustomValidator1_ServerValidate(sender As Object, e As ServerValidateEventArgs)
...
End Sub
Sub CustomValidator2_ServerValidate(sender As Object, e As ServerValidateEventArgs)
...
End Sub
Sub CustomValidator1_ServerValidate_1(sender As Object, e As ServerValidateEventArgs)
...
End Sub
Sub lnkErreursToFormulaire_Click(sender As Object, e As EventArgs)
' відображає вигляд форми
afficheVues(true,false,false)
' повторно виконує перевірку валідності
Page.validate
End Sub
Sub btnEnvoyer_Click(sender As Object, e As EventArgs)
' чи є сторінка валідною?
if not Page.IsValid then
' відображається представлення [erreurs]
afficheVues(false,true,false)
else
' інакше — вигляд [infos]
lblInfo.Text="Le dossier de candidature au DESS IAIE a été envoyé à l'adresse ["+ _
txtMel.Text + "]. Nous vous en souhaitons bonne réception.<br><br>Le secrétariat du DESS."
afficheVues(false,false,true)
end if
end sub
</script>
<html>
...
</html>
- У процедурі [Page_Load], яка виконується при кожному запиті клієнта, ми відображаємо вікно [formulaire], а інші приховуємо. Це робиться лише під час першого запиту. Процедура викликає допоміжну процедуру [afficheVues], якій передаються три логічні значення для того, щоб відобразити або приховати ці три подання.
- Коли викликається процедура [btnEnvoyer_Click], перевірка даних вже виконана. Кнопка [btnEnvoyer] має властивість [CausesValidation=true], яка змушує виконувати цю перевірку даних. Усі елементи перевірки були виконані, і їхня властивість [IsValid] встановлена. Вона вказує, чи були дані, перевірені елементом, дійсними чи ні. Крім того, властивість [IsValid] самої сторінки також була встановлена. Вона приймає значення [vrai] лише в тому випадку, якщо властивість [IsValid] усіх елементів перевірки на сторінці має значення [vrai]. Таким чином, процедура [btnEnvoyer_Click] починається з перевірки того, чи є сторінка валідною. Якщо вона невалідна, відображається представлення [erreurs]. Вона містить елемент управління [ValidationSummary], який переймає атрибути [ErrorMessage] від усіх елементів управління (див. знімок екрана вище). Якщо сторінка є валідною, відображається представлення [infos] разом з інформаційним повідомленням.
- Процедура [lnkErreursToFormulaire_Click] відповідає за відображення подання [formulaire] у тому стані, в якому воно було перевірено. Усі поля введення даних у поданні [formulaire], що мають властивість [EnableViewState=true], автоматично оновлюються. Цікаво, що стан компонентів перевірки не відновлюється. Дійсно, можна було очікувати, що після повернення до подання [erreurs] елементи управління з помилками відображатимуть своє поле [Text]. Це не так. Тому ми примусово провели перевірку даних, використовуючи метод [Page.Validate] цієї сторінки. Це потрібно зробити після того, як панель [vueFormulaire] стала видимою. Отже, загалом відбувається дві перевірки. На практиці цього слід уникати. У даному прикладі це дозволило нам ознайомити вас із новими поняттями щодо перевірки сторінки.
8.3. Компоненти ListControl та зв’язування даних
Деякі з розглянутих серверних компонентів дають змогу відображати список значень (DropDownList, ListBox). Інші, які ми ще не розглядали, дають змогу відображати кілька списків значень у таблицях HTML. Для всіх них можна програмно по черзі пов’язувати значення зі списків із відповідним компонентом. Також можна пов’язувати з цими компонентами більш складні об’єкти, такі як об’єкти типів [Array], [ArrayList], [DataSet], [HashTable], ... що спрощує код, який пов’язує дані з компонентом. Таке пов’язування називається зв’язком даних.
Усі компоненти, похідні від класу [ListControl], можуть бути пов’язані зі списком даних. Це компоненти [DropDownList], [ListBox], [CheckButtonList], [RadioButtonList]. Кожен із цих компонентів може бути пов’язаний із джерелом даних. Це можуть бути різні джерела: [Array], [ArrayList], [DataTable], [DataSet], [HashTable],...., загалом це об’єкт, що реалізує один із інтерфейсів IEnumerable, ICollection, IListSource. Ми розглянемо лише деякі з них. Об’єкт [DataSet] — це об’єкт-зображення реляційної бази даних. Отже, це сукупність таблиць, пов’язаних між собою відношеннями. Об’єкт [DataTable] представляє таку таблицю. Джерело даних присвоює властивості [Text] та [Value] кожному з елементів [Item] об’єкта [ListControl]. Якщо T — це значення [Text], а V — значення [Value], то тег HTML, що генерується для кожного елемента [ListControl], має такий вигляд:
<option value="V">T</option> | |
<input type="checkbox" value="V">T | |
<input type="radio" value="V">T |
Прив'язка компонента [ListControl] до джерела даних здійснюється за допомогою таких властивостей:
джерело даних [Array], [ArrayList], [DataTable], [DataSet], [HashTable], ... | |
у випадку, коли джерелом даних є [DataSet], позначає назву таблиці, яку слід використовувати як джерело даних. У цьому випадку справжнім джерелом даних є таблиця. | |
у випадку, коли джерелом даних є таблиця ([DataTable], [DataSet]), позначає ім’я стовпця таблиці, який надаватиме свої значення полю [Text] елементів [ListControl] | |
у випадку, коли джерелом даних є таблиця ([DataTable], [DataSet]), позначає ім’я стовпця таблиці, який надаватиме свої значення полю [Value] елементів [ListControl] |
Прив’язування компонента [ListControl] до джерела даних не ініціалізує компонент значеннями з джерела даних. Це робить операція [ListControl].DataBind.
Залежно від типу джерела даних, його прив’язування до компонента [ListControl] відбуватиметься по-різному:
[ListControl].DataSource=A поля [Text] та [Value] елементів [ListControl] матимуть значення елементів A | |
[ListControl].DataSource=AL поля [Text] та [Value] елементів [ListControl] матимуть значення елементів AL | |
[ListControl].DataSource=DT, [ListControl].DataTextField = «col1», [ListControl]. DatavalueField = «col2» де col1 і col2 — два стовпці таблиці DT. Поля [Text] та [Value] елементів таблиці [ListControl] матимуть значення, що відповідають значенням стовпців col1 та col2 таблиці DT | |
[ListControl].DataSource=DS, [ListControl].DataSource="table", де "table" — це назва однієї з таблиць DS. [ListControl].DataTextField = «col1», [ListControl]. DatavalueField = «col2», де col1 і col2 — два стовпці таблиці «table». Поля [Text] та [Value] елементів [ListControl] матимуть значення, що відповідають стовпцям col1 та col2 таблиці «table» | |
[ListControl].DataSource=HT, [ListControl].DataTextField="key", [ListControl]. DatavalueField ="value", де [key] та [value] є відповідно ключами та значеннями HT. |
Застосуємо цю інформацію на прикладі [databind1.aspx]:
![]() |
Тут ми маємо п’ять зв’язків даних, кожен із яких містить чотири елементи управління типу [DropDownList], [ListBox], [CheckBoxList] та [RadioButtonList]. П’ять розглянутих зв’язків відрізняються за джерелами даних:
канал | джерело даних |
Масив | |
ArrayList | |
DataTable | |
DataSet | |
HashTable |
8.3.1. Код представлення компонентів
Код відображення елементів управління зв’язку 1 є таким:
<td>
<asp:DropDownList id="DropDownList1" runat="server"></asp:DropDownList>
</td>
<td>
<asp:ListBox id="ListBox1" runat="server" SelectionMode="Multiple"></asp:ListBox>
</td>
<td>
<asp:CheckBoxList id="CheckBoxList1" runat="server"></asp:CheckBoxList>
</td>
<td>
<asp:RadioButtonList id="RadioButtonList1" runat="server"></asp:RadioButtonList>
</td>
Номери з'єднань від 2 до 5 збігаються, за винятком номера з'єднання.
8.3.2. Зв’язок із джерелом даних типу «Масив»
Зв’язок чотирьох елементів управління [ListBox], наведених вище, здійснюється наступним чином у процедурі [Page_Load] коду управління:
' загальні дані
dim textes() as string={"un","deux","trois","quatre"}
dim valeurs() as string={"1","2","3","4"}
dim myDataListe as new ArrayList
dim myDataTable as new DataTable("table1")
dim myDataSet as new DataSet
dim myHashTable as new HashTable
' процедура, що виконується під час завантаження сторінки
Sub page_Load(sender As Object, e As EventArgs)
if not IsPostBack then
' створюються джерела даних, які будуть пов'язані з компонентами
createDataSources
' прив'язка до масиву [Array]
bindToArray
' прив'язка до списку [ArrayList]
bindToArrayList
' прив’язка до таблиці [DataTable]
bindToDataTable
' посилання на групу даних [DataSet]
bindToDataSet
' посилання на словник [HashTable]
bindToHashTable
end if
End Sub
sub createDataSources
' створює джерела даних, які будуть пов’язані з компонентами
' arraylist
dim i as integer
for i=0 to textes.length-1
myDataListe.add(textes(i))
next
' datatable
' визначаємо два стовпці
myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
myDataTable.Columns.Add("texte",Type.GetType("System.String"))
' заповнюємо таблицю
dim ligne as DataRow
for i=0 to textes.length-1
ligne=myDataTable.NewRow
ligne("id")=i
ligne("texte")=textes(i)
myDataTable.Rows.Add(ligne)
next
' dataset — одна таблиця
myDataSet.Tables.Add(myDataTable)
' hashtable
for i=0 to textes.length-1
myHashTable.add(valeurs(i),textes(i))
next
end sub
' прив'язка масиву
sub bindToArray
' прив'язка до компонентів
with DropDownList1
.DataSource=textes
.DataBind
end with
with ListBox1
.DataSource=textes
.DataBind
end with
with CheckBoxList1
.DataSource=textes
.DataBind
end with
with RadioButtonList1
.DataSource=textes
.DataBind
end with
' вибір елементів
ListBox1.Items(1).Selected=true
ListBox1.Items(3).Selected=true
CheckBoxList1.Items(0).Selected=true
CheckBoxList1.Items(3).Selected=true
DropDownList1.SelectedIndex=2
RadioButtonList1.SelectedIndex=1
end sub
sub bindToArrayList
....
end sub
sub bindToDataTable
...
end sub
sub bindToDataSet
...
end sub
Прив'язка елементів управління до даних відбувається тут лише під час першого запиту. Надалі елементи управління зберігатимуть свої дані за допомогою механізму [VIEWSTATE]. Прив’язка здійснюється у процедурі [bindToArray]. Оскільки джерело даних має тип [Array], ініціалізується лише поле [DataSource] компонентів [ListControl]. Заповнення елемента управління значеннями з пов’язаного джерела даних здійснюється методом [ListControl].DataBind. Лише після цього об’єкти [ListControl] отримують елементи. Тоді можна вибрати деякі з них.
8.3.3. Підключення до джерела даних типу ArrayList
Джерело даних [myDataListe] ініціалізується у процедурі [createDataSources]:
' глобальні дані
dim textes() as string={"un","deux","trois","quatre"}
dim myDataListe as new ArrayList
..
' процедура, що виконується під час завантаження сторінки
Sub page_Load(sender As Object, e As EventArgs)
if not IsPostBack then
' створюються джерела даних, які будуть пов'язані з компонентами
createDataSources
...
end if
End Sub
sub createDataSources
' створюються джерела даних, які будуть пов'язані з компонентами
' arraylist
dim i as integer
for i=0 to textes.length-1
myDataListe.add(textes(i))
next
...
end sub
Зв'язування чотирьох елементів управління [ListBox] зв'язку 2 здійснюється наступним чином у процедурі [bindToArrayList] коду управління:
' прив'язка arraylist
sub bindToArrayList
' прив'язка до компонентів
with DropDownList2
.DataSource=myDataListe
.DataBind
end with
with ListBox2
.DataSource=myDataListe
.DataBind
end with
with CheckBoxList2
.DataSource=myDataListe
.DataBind
end with
with RadioButtonList2
.DataSource=myDataListe
.DataBind
end with
' вибір елементів
ListBox2.Items(1).Selected=true
ListBox2.Items(3).Selected=true
CheckBoxList2.Items(0).Selected=true
CheckBoxList2.Items(3).Selected=true
DropDownList2.SelectedIndex=2
RadioButtonList2.SelectedIndex=1
end sub
Оскільки джерело даних має тип [ArrayList], ініціалізується лише поле [DataSource] компонентів [ListControl].
8.3.4. Джерело даних типу DataTable
Джерело даних [myDataTable] ініціалізується у процедурі [createDataSources]:
' глобальні дані
dim textes() as string={"un","deux","trois","quatre"}
dim myDataTable as new DataTable("table1")
...
' процедура, що виконується під час завантаження сторінки
Sub page_Load(sender As Object, e As EventArgs)
if not IsPostBack then
' створюються джерела даних, які будуть пов'язані з компонентами
createDataSources
...
end if
End Sub
sub createDataSources
' створюються джерела даних, які будуть пов'язані з компонентами
...
' таблиця даних
' визначаються два стовпці
myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
myDataTable.Columns.Add("texte",Type.GetType("System.String"))
' заповнюємо таблицю
dim ligne as DataRow
for i=0 to textes.length-1
ligne=myDataTable.NewRow
ligne("id")=i
ligne("texte")=textes(i)
myDataTable.Rows.Add(ligne)
next
...
end sub
Спочатку створюємо об’єкт [DataTable] із двома стовпцями [id] та [texte]. Стовпець [id] буде заповнювати поле [Value] елементами з [ListControl], а стовпець [texte] — їхні поля [Text]. Створення таблиці [DataTable] з двома стовпцями відбувається наступним чином:
myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
myDataTable.Columns.Add("texte",Type.GetType("System.String"))
Отже, створюється таблиця з двома стовпцями:
- перший, що називається «id», є цілим числом
- другий, що називається «текст», має тип «рядок символів»
Створивши структуру таблиці, можна заповнити її за допомогою такого коду:
dim ligne as DataRow
for i=0 to textes.length-1
ligne=myDataTable.NewRow
ligne("id")=i
ligne("texte")=textes(i)
myDataTable.Rows.Add(ligne)
next
Стовпець [id] міститиме цілі числа з [0,1,..,n], тоді як стовпець [texte] міститиме значення з масиву [data]. Після цього таблиця [dataListe] заповнюється. Зв’язування чотирьох елементів управління [ListBox] зв’язку 3 здійснюється наступним чином у процедурі [bindToDataTable] коду управління:
sub bindToDataTable
' прив'язка до компонентів
with DropDownList3
.DataSource=myDataTable
.DataValueField="id"
.DataTextField="texte"
.DataBind
end with
with ListBox3
.DataSource=myDataTable
.DataValueField="id"
.DataTextField="texte"
.DataBind
end with
with CheckBoxList3
.DataSource=myDataTable
.DataValueField="id"
.DataTextField="texte"
.DataBind
end with
with RadioButtonList3
.DataSource=myDataTable
.DataValueField="id"
.DataTextField="texte"
.DataBind
end with
' вибір елементів
ListBox3.Items(1).Selected=true
ListBox3.Items(3).Selected=true
CheckBoxList3.Items(0).Selected=true
CheckBoxList3.Items(3).Selected=true
DropDownList3.SelectedIndex=2
RadioButtonList3.SelectedIndex=1
end sub
Кожен компонент [ListControl] пов'язаний з джерелом [myDataTable], причому кожному з них присвоюється:
Таблиця [myDataTable] є джерелом даних. Стовпець [id] цієї таблиці заповнюватиме поля [Value] елементів компонентів, тоді як стовпець [texte] заповнюватиме їхні поля [Text].
8.3.5. Джерело даних типу DataSet
Джерело даних [myDataSet] ініціалізується у процедурі [createDataSources]:
' загальні дані
dim myDataTable as new DataTable("table1")
dim myDataSet as new DataSet
...
' процедура, що виконується під час завантаження сторінки
Sub page_Load(sender As Object, e As EventArgs)
if not IsPostBack then
' створюються джерела даних, які будуть пов'язані з компонентами
createDataSources
...
end if
End Sub
sub createDataSources
' створюються джерела даних, які будуть пов'язані з компонентами
' набір даних — одна таблиця
myDataSet.Tables.Add(myDataTable)
...
end sub
Об'єкт [DataSet] представляє сукупність таблиць типу [DataTable]. Таблицю [myDataTable], створену раніше, додаємо до [DataSet]. Зв’язування чотирьох елементів управління [ListBox] зв’язку 4 здійснюється наступним чином у процедурі [bindToDataSet] коду управління:
sub bindToDataSet
' прив’язування до компонентів
with DropDownList4
.DataSource=myDataSet
.DataMember="table1"
.DataValueField="id"
.DataTextField="texte"
.DataBind
end with
with ListBox4
.DataSource=myDataSet
.DataMember="table1"
.DataValueField="id"
.DataTextField="texte"
.DataBind
end with
with CheckBoxList4
.DataSource=myDataSet
.DataMember="table1"
.DataValueField="id"
.DataTextField="texte"
.DataBind
end with
with RadioButtonList4
.DataSource=myDataSet
.DataMember="table1"
.DataValueField="id"
.DataTextField="texte"
.DataBind
end with
' вибір елементів
ListBox4.Items(1).Selected=true
ListBox4.Items(3).Selected=true
CheckBoxList4.Items(0).Selected=true
CheckBoxList4.Items(3).Selected=true
DropDownList4.SelectedIndex=2
RadioButtonList4.SelectedIndex=1
end sub
Кожен компонент [ListControl] пов'язаний із джерелом даних таким чином:
Група даних [myDataSet] є джерелом даних. Оскільки вона може містити кілька таблиць, у [DataMember] вказується назва тієї таблиці, яку буде використовуватися. Стовпець [id] цієї таблиці заповнюватиме поля [Value] елементів компонентів, тоді як стовпець [texte] заповнюватиме їхні поля [Text].
8.3.6. Джерело даних типу HashTable
Джерело даних [myHashTable] ініціалізується у процедурі [createDataSources]:
' глобальні дані
dim textes() as string={"un","deux","trois","quatre"}
dim valeurs() as string={"1","2","3","4"}
dim myHashTable as new HashTable
...
' процедура, що виконується під час завантаження сторінки
Sub page_Load(sender As Object, e As EventArgs)
if not IsPostBack then
' створюються джерела даних, які будуть пов'язані з компонентами
createDataSources
...
end if
End Sub
sub createDataSources
' створюються джерела даних, які будуть пов'язані з компонентами
...
' хеш-таблиця
for i=0 to textes.length-1
myHashTable.add(valeurs(i),textes(i))
next
end sub
Словник [myHashTable] можна розглядати як таблицю з двома стовпцями, названими «key» та «value». Стовпець [key] містить ключі словника, а стовпець [value] — значення, пов’язані з ними. Стовпець [key] утворений вмістом масиву [valeurs], а стовпець [value] — вмістом масиву [textes]. Зв’язок цього джерела з елементами управління здійснюється у процедурі [bindToHashTable]:
sub bindToHashTable
' прив'язка до компонентів
with DropDownList5
.DataSource=myHashTable
.DataValueField="key"
.DataTextField="value"
.DataBind
end with
with ListBox5
.DataSource=myHashTable
.DataValueField="key"
.DataTextField="value"
.DataBind
end with
with CheckBoxList5
.DataSource=myHashTable
.DataValueField="key"
.DataTextField="value"
.DataBind
end with
with RadioButtonList5
.DataSource=myHashTable
.DataValueField="key"
.DataTextField="value"
.DataBind
end with
' вибір елементів
ListBox5.Items(1).Selected=true
ListBox5.Items(3).Selected=true
CheckBoxList5.Items(0).Selected=true
CheckBoxList5.Items(3).Selected=true
DropDownList5.SelectedIndex=2
RadioButtonList5.SelectedIndex=1
end sub
Для кожного з компонентів зв'язок здійснюється за допомогою таких інструкцій:
Джерелом даних є словник [myHashTable]. Значення елементів управління надаються стовпцем [key] словника, а тексти — стовпцем [value]. Вставлення елементів словника в елементи управління відбувається у порядку ключів, який, як правило, є випадковим.
8.3.7. Інструкції щодо імпорту просторів імен
Деякі простори імен автоматично імпортуються на сторінку ASP.NET. Це не стосується «System.Data», де знаходяться класи [DataTable] та [DataSet]. Тому цей клас потрібно імпортувати. Це робиться наступним чином:
<%@ Page Language="VB" %>
<%@ import Namespace="System.Data" %>
<script runat="server">
...
</script>
<html>
...
</html>
8.4. Компонент DataGrid та зв'язок даних
Компонент [DataGrid] дозволяє відображати дані у вигляді таблиць, але його можливості виходять далеко за межі простого відображення:
- він надає можливість точно налаштувати «візуальне відображення» таблиці
- він дозволяє оновлювати джерело даних
Компонент [DataGrid] є одночасно потужним і складним. Ми розглянемо його поетапно.
8.4.1. Відображення джерела даних типу Array, ArrayList, DataTable, DataSet
Компонент [DataGrid] дозволяє відображати в таблиці HTML джерела даних типів [Array], [ArrayList], [DataTable], [DataSet]. Для цих чотирьох типів даних достатньо пов’язати джерело з властивістю [DataSource] компонента [DataGrid]:
джерело даних [Array], [ArrayList], [DataTable], [DataSet], ... | |
у випадку, коли джерелом даних є [DataSet], вказує назву таблиці, яку слід використовувати як джерело даних. У такому разі справжнім джерелом даних є таблиця. Якщо це поле не заповнено, відображаються всі таблиці з [DataSet]. |
Тепер ми покажемо сторінку [datagrid1.aspx], на якій показано зв’язок [DataGrid] із чотирма різними джерелами даних:

Сторінка містить чотири компоненти [DataGrid], створені на основі [WebMatrix] таким чином. Компонент розміщується на своєму місці у вкладці [Design]:

Після цього малюється загальна таблиця HTML. Властивості [DataGrid] можна визначити на етапі проектування. Саме це ми й робимо тут для його властивостей форматування. Для цього ми виділяємо [DataGrid], який потрібно налаштувати. Його властивості з’являються у вікні внизу праворуч:

Ми скористаємося двома посиланнями, наведеними вище. Посилання [Générateur de propriétés] надає доступ до основних властивостей [DataGrid]:

Ми знімаємо галочку з пункту [Afficher l'en-tête] для чотирьох компонентів [DataGrid] і підтверджуємо зміни на сторінці. Інше посилання [Mise en forme automatique] дозволяє вибрати один із декількох стилів для таблиці HTML, яка буде відображатися:

Ми обираємо [couleur i] для [DataGrid] № i. Ці параметри оформлення відображаються у коді візуального представлення сторінки:
<html>
<head>
</head>
<body>
<form runat="server">
<p>
Liaison de données avec un DataGrid
</p>
<hr />
<p>
<table>
<tbody>
</tbody>
</table>
<table border="1">
<tbody>
<tr>
<td>
Array</td>
<td>
ArrayList</td>
<td>
DataTable</td>
<td>
DataSet</td>
</tr>
<tr>
<td>
<asp:DataGrid id="DataGrid1" runat="server" ShowHeader="False" CellPadding="4" BackColor="White" BorderColor="#CC9966" BorderWidth="1px" BorderStyle="None">
<FooterStyle forecolor="#330099" backcolor="#FFFFCC"></FooterStyle>
<HeaderStyle font-bold="True" forecolor="#FFFFCC" backcolor="#990000"></HeaderStyle>
<PagerStyle horizontalalign="Center" forecolor="#330099" backcolor="#FFFFCC"></PagerStyle>
<SelectedItemStyle font-bold="True" forecolor="#663399" backcolor="#FFCC66"></SelectedItemStyle>
<ItemStyle forecolor="#330099" backcolor="White"></ItemStyle>
</asp:DataGrid>
</td>
<td>
<asp:DataGrid id="DataGrid2" runat="server" ShowHeader="False" CellPadding="4" BackColor="White" BorderColor="#3366CC" BorderWidth="1px" BorderStyle="None">
<FooterStyle forecolor="#003399" backcolor="#99CCCC"></FooterStyle>
<HeaderStyle font-bold="True" forecolor="#CCCCFF" backcolor="#003399"></HeaderStyle>
<PagerStyle horizontalalign="Left" forecolor="#003399" backcolor="#99CCCC" mode="NumericPages"></PagerStyle>
<SelectedItemStyle font-bold="True" forecolor="#CCFF99" backcolor="#009999"></SelectedItemStyle>
<ItemStyle forecolor="#003399" backcolor="White"></ItemStyle>
</asp:DataGrid>
</td>
<td>
<asp:DataGrid id="DataGrid3" runat="server" ShowHeader="False" CellPadding="3" BackColor="#DEBA84" BorderColor="#DEBA84" BorderWidth="1px" BorderStyle="None" CellSpacing="2">
<FooterStyle forecolor="#8C4510" backcolor="#F7DFB5"></FooterStyle>
<HeaderStyle font-bold="True" forecolor="White" backcolor="#A55129"></HeaderStyle>
<PagerStyle horizontalalign="Center" forecolor="#8C4510" mode="NumericPages"></PagerStyle>
<SelectedItemStyle font-bold="True" forecolor="White" backcolor="#738A9C"></SelectedItemStyle>
<ItemStyle forecolor="#8C4510" backcolor="#FFF7E7"></ItemStyle>
</asp:DataGrid>
</td>
<td>
<asp:DataGrid id="DataGrid4" runat="server" ShowHeader="False" CellPadding="3" BackColor="White" BorderColor="#E7E7FF" BorderWidth="1px" BorderStyle="None" GridLines="Horizontal">
<FooterStyle forecolor="#4A3C8C" backcolor="#B5C7DE"></FooterStyle>
<HeaderStyle font-bold="True" forecolor="#F7F7F7" backcolor="#4A3C8C"></HeaderStyle>
<PagerStyle horizontalalign="Right" forecolor="#4A3C8C" backcolor="#E7E7FF" mode="NumericPages"></PagerStyle>
<SelectedItemStyle font-bold="True" forecolor="#F7F7F7" backcolor="#738A9C"></SelectedItemStyle>
<AlternatingItemStyle backcolor="#F7F7F7"></AlternatingItemStyle>
<ItemStyle forecolor="#4A3C8C" backcolor="#E7E7FF"></ItemStyle>
</asp:DataGrid>
</td>
</tr>
</tbody>
</table>
</p>
<asp:Button id="Button1" runat="server" Text="Envoyer"></asp:Button>
</form>
</body>
</html>
У режимі проектування ми встановили лише властивості форматування. Саме в контрольному коді ми пов'язуємо дані з чотирма компонентами:
<%@ Page Language="VB" %>
<%@ import Namespace="system.data" %>
<script runat="server">
' загальні дані
dim textes1() as string={"un","deux","trois","quatre"}
dim textes2() as string={"one","two","three","for"}
dim valeurs() as string={"1","2","3","4"}
dim myDataListe as new ArrayList
dim myDataTable as new DataTable("table1")
dim myDataSet as new DataSet
' процедура, що виконується під час завантаження сторінки
Sub page_Load(sender As Object, e As EventArgs)
if not IsPostBack then
' створюються джерела даних, які будуть пов'язані з компонентами
createDataSources
' прив'язка до масиву [Array]
bindToArray
' прив'язка до списку [ArrayList]
bindToArrayList
' прив’язка до таблиці [DataTable]
bindToDataTable
' посилання на групу даних [DataSet]
bindToDataSet
end if
End Sub
sub createDataSources
' створює джерела даних, які будуть пов’язані з компонентами
' arraylist
dim i as integer
for i=0 to textes1.length-1
myDataListe.add(textes1(i))
next
' datatable
' визначаємо два стовпці
myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
myDataTable.Columns.Add("texte1",Type.GetType("System.String"))
myDataTable.Columns.Add("texte2",Type.GetType("System.String"))
' заповнюємо таблицю
dim ligne as DataRow
for i=0 to textes1.length-1
ligne=myDataTable.NewRow
ligne("id")=valeurs(i)
ligne("texte1")=textes1(i)
ligne("texte2")=textes2(i)
myDataTable.Rows.Add(ligne)
next
' dataset — одна таблиця
myDataSet.Tables.Add(myDataTable)
end sub
' зв'язування масиву
sub bindToArray
with DataGrid1
.DataSource=textes1
.DataBind
end with
end sub
' зв'язок з arraylist
sub bindToArrayList
with DataGrid2
.DataSource=myDataListe
.DataBind
end with
end sub
' зв'язок з таблицею даних
sub bindToDataTable
with DataGrid3
.DataSource=myDataTable
.DataBind
end with
end sub
' зв'язок набору даних
sub bindToDataSet
with DataGrid4
.DataSource=myDataSet
.DataBind
end with
end sub
</script>
<html>
...
</html>
Код досить схожий на той, що наведено в попередньому прикладі, тому ми не будемо його окремо коментувати. Однак звернімо увагу на те, як здійснюється зв’язування даних. Для кожного з чотирьох елементів управління достатньо такої послідовності:
де [source de données] має вигляд [Array] або [ArrayList] або [DataTable] або [DataSet]. Отже, бачимо, що маємо потужний інструмент для відображення даних у таблицях:
- форматування здійснюється за допомогою [WebMatrix] або будь-якого іншого IDE на етапі проектування
- підключення даних здійснюється в коді. За допомогою [WebMatrix] це можна зробити на етапі проектування, якщо джерелом даних є серверна база даних SQL або база даних ACCESS. У цьому випадку на аркуші розміщується компонент типу [SqlDataSource] або [AccessDataSource]. Цей компонент можна під час проектування підключити до фізичного джерела даних — бази даних сервера SQL або бази даних ACCESS, залежно від конкретного випадку. Якщо властивості [DataSource] компонента [DataGrid] присвоїти об’єкт [SqlDataSource] або [AccessDataSource], пов’язаний із фізичним джерелом, то в режимі проектування фактичні дані у компоненті [DataGrid].
8.5. ViewState компонентів списків даних
Тут ми пропонуємо розглянути механізм роботи [VIEWSATE] для компонентів списків даних. Можна вагатися між двома методами збереження стану компонента списку даних між двома запитами клієнта:
- встановити його атрибут [VIEWSTATE] у значення «true»
- встановити його атрибут [VIEWSTATE] у значення «false» та зберегти його джерело даних у сесії, щоб мати змогу пов’язати компонент із цим джерелом під час наступного запиту.
Наступний приклад ілюструє деякі аспекти механізму роботи контейнерів даних [VIEWSTATE]. Під час першого запиту клієнта додаток відображає такий вигляд:
![]() |
№ | назва | тип | властивості | роль |
1 | DataGrid | EnableViewState=true | відображає джерело даних S | |
2 | DataGrid | EnableViewState=true | відображає те саме джерело S, що й [DataGrid1] | |
3 | Button | EnableViewState=false | кнопка [submit] |
Компонент [DataGrid1] підтримується механізмом [VIEWSTATE]. Потрібно з’ясувати, чи цей механізм, який оновлює відображення [DataGrid1] при кожному запиті, також оновлює його джерело даних. Для цього джерело даних пов’язане з компонентом [DataGrid2]. Генерація останнього при кожному запиті здійснюється за допомогою явного зв’язку з джерелом даних [DataGrid1]. Він також має атрибут [EnableViewState] у [vrai].
Код представлення [main.aspx] для цього додатка виглядає наступним чином:
<%@ page src="main.aspx.vb" inherits="main" autoeventwireup="false" %>
<HTML>
<HEAD>
<title></title>
</HEAD>
<body>
<form runat="server">
<table>
<tr>
<td align="center">DataGrid 1</td>
<td align="center">
DataGrid 2</td>
</tr>
<tr>
<td>
<asp:DataGrid id="DataGrid1" runat="server" ...>
<SelectedItemStyle ...></SelectedItemStyle>
....
</asp:DataGrid></td>
<td>
<asp:DataGrid id="Datagrid2" runat="server" ...>
....
</asp:DataGrid></td>
</tr>
</table>
<asp:Button id="Button1" runat="server" Text="Envoyer"></asp:Button>
</form>
</body>
</HTML>
Контролер [main.aspx.vb] має такий вигляд:
Imports System.Data
Public Class main
Inherits System.Web.UI.Page
Protected WithEvents DataGrid1 As System.Web.UI.WebControls.DataGrid
Protected WithEvents Datagrid2 As System.Web.UI.WebControls.DataGrid
Protected WithEvents Button1 As System.Web.UI.WebControls.Button
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' у першому запиті джерело даних визначено та пов’язано з першою сіткою даних
If Not IsPostBack Then
'визначити джерело даних
With DataGrid1
.DataSource = createDataSource()
.DataBind()
End With
End If
' при кожному запиті сітка даних 2 пов'язується з джерелом даних першої сітки
With Datagrid2
.DataSource = DataGrid1.DataSource
.DataBind()
End With
End Sub
Private Function createDataSource() As DataTable
' ініціалізується джерело даних
Dim thèmes As New DataTable
' стовпці
With thèmes.Columns
.Add("id", GetType(System.Int32))
.Add("thème", GetType(System.String))
.Add("description", GetType(System.String))
End With
' стовпець id буде первинним ключем
thèmes.Constraints.Add("cléprimaire", thèmes.Columns("id"), True)
' рядки
Dim ligne As DataRow
For i As Integer = 0 To 4
ligne = thèmes.NewRow
ligne.Item("id") = i.ToString
ligne.Item("thème") = "thème" + i.ToString
ligne.Item("description") = "description du thème " + i.ToString
thèmes.Rows.Add(ligne)
Next
Return thèmes
End Function
Private Sub InitializeComponent()
End Sub
End Class
Метод [createDataSource] створює джерело S типу [DataTable]. Ми не будемо зупинятися на його коді, оскільки він не є предметом цього прикладу. Нас цікавить саме спосіб побудови двох компонентів [DataGrid]:
- компонент [DataGrid1] пов’язується з таблицею S один раз — під час першого запиту. Потім це зв’язування зникає.
- компонент [DataGrid2] пов’язується з джерелом [DataGrid1.DataSource] під час кожного нового запиту.
Під час першого запиту ми отримуємо таке зображення:

Цілком логічно, що обидва компоненти відображають джерело даних, до якого вони були прив’язані. Ми використовуємо кнопку [Envoyer], щоб ініціювати запит [PostBack] до сервера. Отримане вікно виглядає так:

Ми бачимо, що компонент [DataGrid1] зберіг своє значення, на відміну від компонента [DataGrid2]. Пояснення:
- ще до запуску процедури [Page_Load] об’єкти [DataGrid1] та [DataGrid2] відновили значення, які вони мали під час попереднього запиту, завдяки механізму [viewstate]. Дійсно, у обох об’єктів властивість [EnableViewState] має значення [vrai].
- Виконується процедура [Page_Load]. Оскільки маємо справу з операцією [PostBack], компонент [DataGrid1] не змінюється процедурою [Page_Load] (див. код). Тому він зберігає значення, отримане за допомогою [viewstate]. Це показано на екрані вище.
- Компонент [DataGrid2], у свою чергу, пов’язаний через [DataBind] із джерелом даних [DataGrid1.DataSource]. Отже, він відновлюється, і значення, яке він щойно отримав за допомогою [viewstate], втрачається. Тому в даному випадку було б доцільно, щоб його властивість [EnableViewState] мала значення [faux], щоб уникнути зайвого управління його станом. На екрані вище видно, що [DataGrid2] було пов’язано з порожнім джерелом. Оскільки цим джерелом є [DataGrid1.DataSource], звідси випливає, що хоча механізм [viewstate] і відновлює відображення компонента [DataGrid1], він не відновлює його властивості, такі як [DataSource].
Який висновок можна зробити з цього прикладу? Не слід встановлювати властивість [EnableViewState] контейнера даних на [vrai], якщо він має бути пов’язаний (DataBind) із джерелом даних під час кожного запиту. Однак існують певні випадки, коли навіть у такій ситуації властивість [EnableViewState] контейнера має залишатися на рівні [vrai], інакше події, які ми хотіли б обробляти, не спрацьовуватимуть. Ми матимемо нагоду ознайомитися з одним із таких прикладів.
Часто джерело даних контейнера змінюється у міру надходження запитів. Тому під час кожного запиту необхідно пов’язувати контейнер із джерелом даних. Часто джерело даних зберігається у сесії, щоб запити мали до нього доступ. Наш другий приклад ілюструє цей механізм. Додаток надає лише один вигляд:
![]() |
№ | назва | тип | властивості | роль |
1 | DataGrid | EnableViewState=true | відображає джерело даних S | |
2 | DataGrid | EnableViewState=false | відображає те саме джерело S, що й [DataGrid1] | |
3 | Button | EnableViewState=false | кнопка [submit] | |
4 | Етикетка | EnableViewState=false | інформаційні тексти |
Компонент [DataGrid1] пов'язується з даними лише під час першого запиту. Він зберігатиме своє значення протягом усіх запитів завдяки механізму [viewstate]. Компонент [DataGrid2] пов'язується під час кожного запиту з джерелом даних, до якого під час кожного нового запиту додається новий елемент. Тому необхідно пов'язати (DataBind) компонент [DataGrid2] з кожним запитом. Тому ми встановили його атрибут [EnableViewState] на [faux], як було рекомендовано раніше. Таким чином, під час другого запиту (за допомогою кнопки [Envoyer]) ми отримуємо таку відповідь:

Компонент [DataGrid1] зберіг своє початкове значення. Компонент [DataGrid2] має на один елемент більше. Три записи [1,2,2] відповідають номеру запиту. Бачимо, що один із записів є помилковим. Спробуємо з’ясувати, чому.
Код представлення [main.aspx] додатка має такий вигляд:
<%@ Page src="main.aspx.vb" inherits="main" autoeventwireup="false" Language="vb" %>
<HTML>
<HEAD>
<title></title>
</HEAD>
<body>
<form runat="server">
<table>
<tr>
<td align="center" bgColor="#ccffcc">DataGrid 1</td>
<td align="center" bgColor="#ffff99">DataGrid 2</td>
</tr>
<tr>
<td vAlign="top">
<asp:DataGrid id="DataGrid1" runat="server" ...>
...
</asp:DataGrid>
</td>
<td vAlign="top">
<asp:DataGrid id="Datagrid2" runat="server" ... EnableViewState="False">
....
</asp:DataGrid>
</td>
</tr>
</table>
<P>Numéro de requête :
<asp:Label id="lblInfo1" runat="server" EnableViewState="False"></asp:Label>,
<asp:Label id="lblInfo2" runat="server" EnableViewState="False"></asp:Label>,
<asp:Label id="lblInfo3" runat="server" EnableViewState="False"></asp:Label></P>
<P>
<asp:Button id="Button1" runat="server" Text="Envoyer" EnableViewState="False"></asp:Button></P>
</form>
</body>
</HTML>
Код контролера [main.aspx.vb] такий:
Imports System.Data
Imports System
Public Class main
Inherits System.Web.UI.Page
Protected WithEvents DataGrid1 As System.Web.UI.WebControls.DataGrid
Protected WithEvents Datagrid2 As System.Web.UI.WebControls.DataGrid
Protected WithEvents Button1 As System.Web.UI.WebControls.Button
Protected WithEvents lblInfo1 As System.Web.UI.WebControls.Label
Protected WithEvents lblInfo2 As System.Web.UI.WebControls.Label
Protected WithEvents lblInfo3 As System.Web.UI.WebControls.Label
Dim dtThèmes As DataTable
Dim numRequête1 As Integer
Dim numRequête2 As Integer
Dim numRequête3 As New entier
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' у першому запиті джерело даних визначено та пов’язано з першою таблицею даних
If Not IsPostBack Then
'визначити джерело даних
dtThèmes = createDataSource()
With DataGrid1
.DataSource = dtThèmes
.DataBind()
End With
' зберегти інформацію в сесії
Session("source") = dtThèmes
numRequête1 = 0 : Session("numRequête1") = numRequête1
numRequête2 = 0 : Session("numRequête2") = numRequête2
numRequête3.valeur = 0 : Session("numRequête3") = numRequête3
End If
' при кожному запиті додається нова тема
dtThèmes = CType(Session("source"), DataTable)
Dim nbThèmes = dtThèmes.Rows.Count
Dim ligne As DataRow = dtThèmes.NewRow
With ligne
.Item("id") = nbThèmes.ToString
.Item("thème") = "thème" + nbThèmes.ToString
.Item("description") = "description du thème " + nbThèmes.ToString
End With
dtThèmes.Rows.Add(ligne)
'пов'язує datagrid2 з джерелом даних
With Datagrid2
.DataSource = dtThèmes
.DataBind()
End With
' інформація про кількість запитів
numRequête1 = CType(Session("numRequête1"), Integer)
numRequête1 += 1
lblInfo1.Text = numRequête1.ToString
numRequête2 = CType(Session("numRequête2"), Integer)
numRequête2 += 1
lblInfo2.Text = numRequête2.ToString
numRequête3 = CType(Session("numRequête3"), entier)
numRequête3.valeur += 1
lblInfo3.Text = numRequête3.valeur.ToString
' зберігаємо деяку інформацію в сесії
Session("numRequête2") = numRequête2
End Sub
Private Function createDataSource() As DataTable
....
End Function
End Class
Public Class entier
Private _valeur As Integer
Public Property valeur() As Integer
Get
Return _valeur
End Get
Set(ByVal Value As Integer)
_valeur = Value
End Set
End Property
End Class
Ми не наводимо код методу [createDataSource]. Він такий самий, як і в попередньому додатку, за винятком того, що в вихідному коді міститься лише три рядки. Спочатку розглянемо управління джерелом даних та двома контейнерами:
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' під час першого запиту джерело даних визначається та пов'язується з першою таблицею даних
If Not IsPostBack Then
'визначити джерело даних
dtThèmes = createDataSource()
With DataGrid1
.DataSource = dtThèmes
.DataBind()
End With
' збереження інформації в сесії
Session("source") = dtThèmes
...
End If
' при кожному запиті додається нова тема
dtThèmes = CType(Session("source"), DataTable)
Dim nbThèmes = dtThèmes.Rows.Count
Dim ligne As DataRow = dtThèmes.NewRow
With ligne
.Item("id") = nbThèmes.ToString
.Item("thème") = "thème" + nbThèmes.ToString
.Item("description") = "description du thème " + nbThèmes.ToString
End With
dtThèmes.Rows.Add(ligne)
'пов'язує datagrid2 з джерелом даних
With Datagrid2
.DataSource = dtThèmes
.DataBind()
End With
...
End Sub
Компонент [DataGrid1] пов'язується з джерелом даних S лише під час першого запиту (не IsPostBack). Тоді це джерело даних розміщується в сесії. Надалі це більше не відбуватиметься. Джерело даних S, розміщене в сесії, отримується під час кожного запиту, і до нього додається новий рядок. Компонент [DataGrid2] явно пов’язується з джерелом S під час кожного запиту. Саме тому його вміст збільшується на один рядок під час кожного запиту. Можна помітити, що після зміни вмісту джерела S його не повертають явно до сесії за допомогою операції:
Чому? Коли запускається запит, сесія містить об’єкт Session("source") типу [DataTable], який є джерелом даних у тому вигляді, в якому воно було під час останнього запиту. Назвемо об’єкт Session("source") S. Коли ми пишемо:
dtThèmes = CType(Session("source"), DataTable)
[dtThèmes] та [S] — це два посилання на один і той самий об’єкт [DataTable]. Отже, коли код [Page_Load] додає елемент до таблиці, на яку посилається [dtThèmes], він одночасно додає його до таблиці, на яку посилається [S]. Після завершення виконання сторінки всі об’єкти, присутні в сесії, будуть збережені, а отже, об’єкт Session("source"), c.a.d. S, c.a.d, [dtThèmes]. Отже, саме новий вміст джерела даних зберігається. Не було потреби писати:
щоб виконати це збереження, оскільки Session("source") вже дорівнює [dtThèmes]. Це вже не справджується, коли дані, що зберігаються в сесії, не є об’єктами, наприклад, структури [Integer, Float, ...]. Про це свідчить управління лічильниками запитів:
Dim numRequête1 As Integer
Dim numRequête2 As Integer
Dim numRequête3 As New entier
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' при першому запиті джерело даних визначається та пов'язується з першою таблицею даних
....
' збереження інформації в сесії
Session("source") = dtThèmes
numRequête1 = 0 : Session("numRequête1") = numRequête1
numRequête2 = 0 : Session("numRequête2") = numRequête2
numRequête3.valeur = 0 : Session("numRequête3") = numRequête3
End If
' при кожному запиті додається нова тема
....
' інформація про кількість запитів
numRequête1 = CType(Session("numRequête1"), Integer)
numRequête1 += 1
lblInfo1.Text = numRequête1.ToString
numRequête2 = CType(Session("numRequête2"), Integer)
numRequête2 += 1
lblInfo2.Text = numRequête2.ToString
numRequête3 = CType(Session("numRequête3"), entier)
numRequête3.valeur += 1
lblInfo3.Text = numRequête3.valeur.ToString
' зберігання певної інформації в сесії
Session("numRequête2") = numRequête2
End Sub
Private Function createDataSource() As DataTable
....
End Function
Private Sub InitializeComponent()
End Sub
End Class
Public Class entier
Private _valeur As Integer
Public Property valeur() As Integer
Get
Return _valeur
End Get
Set(ByVal Value As Integer)
_valeur = Value
End Set
End Property
Лічильники запитів зберігаються у трьох елементах:
- numRequête1 та numRequête2 типу [Integer] — [Integer] не є класом, а структурою
- numRequête3 типу [entier] — [entier] є класом, визначеним спеціально для цього випадку
Коли ми пишемо:
numRequête1 = CType(Session("numRequête1"), Integer)
..
numRequête2 = CType(Session("numRequête2"), Integer)
..
numRequête3 = CType(Session("numRequête3"), entier)
..
- структура [Session("numRequête1")] копіюється в [numRequête1]. Таким чином, коли елемент [numRequête1] змінюється, елемент [Session("numRequête1")] не змінюється
- те саме стосується [Session("numRequête2")] та [numRequête2]
- Елементи [Session("numRequête3")] та [numRequête3] є двома посиланнями на один і той самий об’єкт типу [entier]. Об’єкт, на який посилаються ці елементи, може бути змінений як через одне, так і через інше посилання.
З цього випливає, що немає сенсу писати:
для збереження нового значення [numRequête3] у сесії. Натомість слід написати:
щоб зберегти нові значення структур [numRequête1] та [numRequête2]. Ми робимо це лише для [numRequête2], що пояснює, чому на знімку екрана, отриманому після виконання другого запиту, лічильник [numRequête1] є помилковим.
Отже, слід пам’ятати, що після завантаження даних у сесію їх не потрібно повторно завантажувати, якщо вони представлені об’єктом. В інших випадках це потрібно робити, якщо дані були змінені.
8.6. Відображення списку даних за допомогою DataGrid з пагінацією та сортуванням
Компонент [DataGrid] дозволяє відображати вміст [DataSet]. Досі ми завжди створювали наші [DataSet] «вручну». Цього разу ми використовуємо [DataSet], отриманий із бази даних. Ми створюємо таку програму MVC:
![]() |
Ці три види будуть вбудовані в код представлення контролера [main.aspx] у вигляді контейнерів. Отже, цей додаток має єдину сторінку [main.aspx].
8.6.1. Бізнес-класи
Клас [produits] надає доступ до наступної бази даних ACCESS:

Код класу [produits] такий:
Imports System
Imports System.Data
Imports System.Data.OleDb
Imports System.Xml
Namespace st.istia.univangers.fr
Public Class produits
Private chaineConnexionOLEDB As String
Public Sub New(ByVal chaineConnexionOLEDB As String)
' зберігається рядок підключення
Me.chaineConnexionOLEDB = chaineConnexionOLEDB
End Sub
Public Function getDataSet(ByVal commande As String) As DataSet
' створюється об’єкт DataAdapter для зчитування даних із джерела OLEDB
Dim adaptateur As New OleDbDataAdapter(commande, chaineConnexionOLEDB)
' створюється образ результату запиту SELECT у пам'яті
Dim contenu As New DataSet
Try
adaptateur.Fill(contenu)
Catch e As Exception
Throw New Exception("Erreur d'accès à la base de données (" + e.Message + ")")
End Try
' повертається результат
Return contenu
End Function
End Class
End Namespace
База даних ACCESS буде управлятися за допомогою драйвера OLEDB. Конструктору класу [produits] передаються ідентифікатор ланцюга з’єднання, драйвер OLEDB та база даних, яку потрібно керувати. Клас [produits] має метод [getDataSet], який повертає об’єкт [DataSet], отриманий у результаті виконаннязапиту SQL [select], текст якого передається як параметр. Під час виконання методу відбувається кілька операцій: створення з’єднання з базою даних, виконання [select], закриття з’єднання. Все це може спричинити виняток, який тут обробляється. Тестова програма може виглядати так:
Option Explicit On
Option Strict On
' простори імен
Imports System
Imports System.Data
Imports Microsoft.VisualBasic
Namespace st.istia.univangers.fr
' тестова сторінка
Module testproduits
Sub Main(ByVal arguments() As String)
' відображає вміст таблиці товарів
' таблиця знаходиться в базі даних ACCESS, ім'я файлу якої відповідає імені сторінки
Const syntaxe1 As String = "pg bdACCESS"
' перевірка параметрів програми
If arguments.Length <> 1 Then
' повідомлення про помилку
Console.Error.WriteLine(syntaxe1)
' кінець
Environment.Exit(1)
End If
' підготовка рядка з'єднання
Dim chaineConnexion As String = "Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=" + arguments(0)
' створення об’єкта «продукти»
Dim objProduits As produits = New produits(chaineConnexion)
' завантаження таблиці товарів у набір даних
Dim contenu As DataSet
Try
contenu = objProduits.getDataSet("select id,nom,prix from liste")
Catch ex As Exception
Console.Error.WriteLine(("L'erreur suivante s'est produite : " + ex.Message))
Environment.Exit(2)
End Try
' виводиться його вміст
Dim lignes As DataRowCollection = contenu.Tables(0).Rows
For i As Integer = 0 To lignes.Count - 1
' рядок i таблиці
Console.Out.WriteLine(lignes(i).Item("id").ToString + "," + lignes(i).Item("nom").ToString + _
"," + lignes(i).Item("prix").ToString)
Next
End Sub
End Module
End Namespace
Компіляція різних елементів цього додатка відбувається наступним чином:
dos>vbc /t:library /r:system.dll /r:system.data.dll /r:system.xml.dll produits.vb
dos>vbc /r:produits.dll /r:system.data.dll /r:system.xml.dll /r:system.dll testproduits.vb
dos>dir
06/05/2004 16:52 118 784 produits.mdb
07/05/2004 11:07 902 produits.vb
07/04/2004 07:01 1 532 testproduits.vb
07/05/2004 14:21 3 584 produits.dll
07/05/2004 14:22 4 608 testproduits.exe
dos>testproduits produits.mdb
1,produit1,10
2,produit2,20
3,produit3,30
8.6.2. Види
Тепер, коли ми маємо клас для доступу до даних, напишемо контролери та види цього веб-додатку. Спочатку давайте розглянемо, як він працює. Перший вид виглядає так:
![]() |
№ | назва | тип | властивості | роль |
1 | TextBox | EnableViewState=true | поле введення запиту select | |
2 | RequiredFieldValidator | EnableViewState=false | перевіряє наявність 1 | |
3 | TextBox | EnableViewState=true | поле введення — вказує кількість рядків даних, що відображаються на одній сторінці результатів | |
4 | RequiredFieldValidator | EnableViewState=false | перевіряє наявність 3 | |
5 | RangeValidator | EnableViewState=false | перевіряє, чи (3) знаходиться в діапазоні [1,30] | |
6 | Button | EnableViewState=false | кнопка [submit] |
Цей вигляд ми назвемо [formulaire]. Він дозволяє користувачеві виконати запит SQL select у базі даних [produits.mdb]. Виконання запиту створює новий вигляд:
![]() |
№ | назва | тип | властивості | роль |
1 | Мітка | EnableViewState=false | інформаційне поле | |
2 | RadioButton | EnableViewState=false GroupName=rdTri | дозволяє вибрати напрямок сортування | |
3 | DataGrid | EnableViewState=true AllowPaging=true AllowSorting=true | таблиця для відображення результатів вибірки | |
4 | LinkButton | EnableViewState=false | кнопка-посилання [submit] |
Цей вигляд ми назвемо [résultats]. Саме він містить [DataGrid], який відображатиме результат команди SQL select. Користувач може помилитися у своєму запиті. Про деякі помилки йому буде повідомлено у вікні [erreurs] завдяки елементам перевірки.

Про інші помилки йому повідомить вікно [erreurs]:

Відображається вікно [erreurs]:
![]() |
№ | назва | тип | властивості | роль |
1 | змінна | код HTML, необхідний для відображення помилок | ||
3 | LinkButton | EnableViewState=false | кнопка-посилання [submit] |
Три види додатка — це три різні контейнери (панелі) на одній сторінці [main.aspx]. Її код відображення такий:
<%@ Page src="main.aspx.vb" inherits="main" autoeventwireup="false" Language="vb" %>
<HTML>
<HEAD>
</HEAD>
<body>
<P>Liaison de données avec un DataGrid</P>
<HR width="100%" SIZE="1">
<form runat="server">
<asp:panel id="vueFormulaire" runat="server">
<P>Commande [select] à exécuter sur la table LISTE</P>
<P> Exemple : select id, nom, prix from LISTE
</P>
<P>
<asp:TextBox id="txtSelect" runat="server" Columns="60"></asp:TextBox></P>
<P>
<asp:RequiredFieldValidator id="RequiredFieldValidator1" runat="server" EnableViewState="False" EnableClientScript="False"
ErrorMessage="Indiquez la requête [select] à exécuter" ControlToValidate="txtSelect" Display="Dynamic"></asp:RequiredFieldValidator></P>
<P>Nombre de lignes par page :
<asp:TextBox id="txtPages" runat="server" Columns="3"></asp:TextBox></P>
<P>
<asp:RequiredFieldValidator id="RequiredFieldValidator2" runat="server" EnableViewState="False" EnableClientScript="False"
ErrorMessage="Indiquez le nombre de lignes par page désirées" ControlToValidate="txtPages" Display="Dynamic"></asp:RequiredFieldValidator>
<asp:RangeValidator id="RangeValidator1" runat="server" EnableViewState="False" EnableClientScript="False"
ErrorMessage="Vous devez indiquer un nombre entre 1 et 30" ControlToValidate="txtPages" Display="Dynamic"
Type="Integer" MinimumValue="1" MaximumValue="30"></asp:RangeValidator></P>
<P>
<asp:Button id="btnExécuter" runat="server" EnableViewState="False" Text="Exécuter"></asp:Button></P>
</asp:panel>
<asp:Panel id="vueRésultats" runat="server">
<P>Résultats de la requête
<asp:Label id="lblSelect" runat="server"></asp:Label></P>
<P>Tri
<asp:RadioButton id="rdCroissant" runat="server" Text="croissant" Checked="True" GroupName="rdTri"></asp:RadioButton>
<asp:RadioButton id="rdDécroissant" runat="server" Text="décroissant" GroupName="rdTri"></asp:RadioButton></P>
<P>
<asp:DataGrid id="DataGrid1" runat="server" BorderColor="#CC9966" BorderStyle="None" BorderWidth="1px"
BackColor="White" CellPadding="4" AllowPaging="True" PageSize="4" AllowSorting="True">
<SelectedItemStyle Font-Bold="True" ForeColor="#663399" BackColor="#FFCC66"></SelectedItemStyle>
<ItemStyle ForeColor="#330099" BackColor="White"></ItemStyle>
<HeaderStyle Font-Bold="True" ForeColor="#FFFFCC" BackColor="#990000"></HeaderStyle>
<FooterStyle ForeColor="#330099" BackColor="#FFFFCC"></FooterStyle>
<PagerStyle NextPageText="Suivant" PrevPageText="Précédent" HorizontalAlign="Center"
ForeColor="#330099" BackColor="#FFFFCC"></PagerStyle>
</asp:DataGrid></P>
<P>
<asp:LinkButton id="lnkRésultats" runat="server" EnableViewState="False">Retour au formulaire</asp:LinkButton></P>
</asp:Panel>
<asp:Panel id="vueErreurs" runat="server">
<P>Les erreurs suivantes se sont produites :</P>
<% =erreursHTML %>
<P>
<asp:LinkButton id="lnkErreurs" runat="server" EnableViewState="False">Retour vers le formulaire</asp:LinkButton></P>
</asp:Panel>
</form>
</body>
</HTML>
8.6.3. Налаштування DataGrid
Зупинимося на пагінації компонента [DataGrid] — опції, з якою ми стикаємося вперше. У наведеному вище коді ця пагінація керується такими атрибутами:
<asp:DataGrid id="DataGrid1" runat="server" PageSize="4" AllowPaging="True" ...>
...
<PagerStyle NextPageText="Suivant" PrevPageText="Précédent" ...></PagerStyle>
</asp:DataGrid></P>
дозволяє пагінацію | |
чотири рядки даних на сторінку | |
Текст посилання для переходу на наступну сторінку джерела даних | |
Текст посилання для переходу на попередню сторінку джерела даних |
Цю інформацію можна ввести безпосередньо в атрибути тегу <asp:datagrid>. Також можна скористатися [WebMatrix]. У вікні властивостей [DataGrid] перейдіть за посиланням [Générateur de propriétés]:

З’явиться такий майстер:

Вибираємо опцію [Pagination]:

Вище наведено значення атрибутів пагінації [DataGrid] з коду представлення.
Крім того, ми дозволяємо сортування даних за одним із стовпців [DataGrid]. Існують різні способи зробити це. Один із них — задати властивість [AllowSorting=true] у вікні властивостей [DataGrid]. Також можна скористатися генератором властивостей. Незалежно від обраного методу, це призведе до появи атрибута [AllowSorting=true] у тезі <asp:DataGrid> компонента:
<asp:DataGrid id="DataGrid1" runat="server" ... AllowPaging="True" PageSize="4" AllowSorting="True">
8.6.4. Контролери
Контролер [global.asax, global.asax.vb] має такий вигляд:
[global.asax]
[global.asax.vb]
Imports st.istia.univangers.fr
Imports System.Configuration
Public Class Global
Inherits System.Web.HttpApplication
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
' створення об’єкта «товари»
Dim objProduits As produits
Try
objProduits = New produits(ConfigurationSettings.AppSettings("OLEDBStringConnection"))
' додаємо об’єкт до додатка
Application("objProduits") = objProduits
' помилок немає
Application("erreur") = False
Catch ex As Exception
'сталася помилка, її фіксують у додатку
Application("erreur") = True
Application("message") = ex.Message
End Try
End Sub
End Class
Під час запуску додатка (Application_Start) ми створюємо об’єкт [produits] і розміщуємо його в додатку, щоб він був доступний для всіх запитів усіх клієнтів. Якщо під час створення об’єкта виникає виняток, його фіксують у додатку. Процедура [Application_Start] виконається лише один раз. Після цього контролер [global.asax] більше не втручатиметься. Тоді цю роботу виконуватиме контролер [main.aspx.vb]:
Imports System.Collections
Imports Microsoft.VisualBasic
Imports System.Data
Imports st.istia.univangers.fr
Imports System
Imports System.Xml
Public Class main
Inherits System.Web.UI.Page
' компоненти сторінки
Protected WithEvents txtSelect As System.Web.UI.WebControls.TextBox
Protected WithEvents RequiredFieldValidator1 As System.Web.UI.WebControls.RequiredFieldValidator
Protected WithEvents txtPages As System.Web.UI.WebControls.TextBox
Protected WithEvents RequiredFieldValidator2 As System.Web.UI.WebControls.RequiredFieldValidator
Protected WithEvents RangeValidator1 As System.Web.UI.WebControls.RangeValidator
Protected WithEvents btnExécuter As System.Web.UI.WebControls.Button
Protected WithEvents vueFormulaire As System.Web.UI.WebControls.Panel
Protected WithEvents lblSelect As System.Web.UI.WebControls.Label
Protected WithEvents DataGrid1 As System.Web.UI.WebControls.DataGrid
Protected WithEvents lnkRésultats As System.Web.UI.WebControls.LinkButton
Protected WithEvents vueRésultats As System.Web.UI.WebControls.Panel
Protected WithEvents lnkErreurs As System.Web.UI.WebControls.LinkButton
Protected WithEvents vueErreurs As System.Web.UI.WebControls.Panel
Protected WithEvents rdCroissant As System.Web.UI.WebControls.RadioButton
Protected WithEvents rdDécroissant As System.Web.UI.WebControls.RadioButton
' дані сторінки
Protected erreursHTML As String
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' перевіряємо, чи є помилка в додатку
If CType(Application("erreur"), Boolean) Then
' додаток не ініціалізувався належним чином
Dim erreurs As New ArrayList
erreurs.Add("Application momentanément indisponible (" + CType(Application("message"), String) + ")")
afficheErreurs(erreurs, False)
Exit Sub
End If
'перший запит
If Not IsPostBack Then
' відображається порожня форма
afficheFormulaire()
End If
End Sub
Private Sub afficheErreurs(ByVal erreurs As ArrayList, ByVal afficheLien As Boolean)
' відображається сторінка помилок
erreursHTML = ""
For i As Integer = 0 To erreurs.Count - 1
erreursHTML += "<li>" + erreurs(i).ToString + "</li>" + ControlChars.CrLf
Next
lnkErreurs.Visible = afficheLien
' відображається вигляд [erreurs]
vueErreurs.Visible = True
vueFormulaire.Visible = False
vueRésultats.Visible = False
End Sub
Private Sub afficheFormulaire()
' відображається вид [formulaire]
vueFormulaire.Visible = True
vueErreurs.Visible = False
vueRésultats.Visible = False
End Sub
Private Sub afficheRésultats(ByVal sqlTexte As String, ByVal données As DataView)
' ініціалізуються елементи управління
lblSelect.Text = sqlTexte
With DataGrid1
.DataSource = données
.PageSize = CType(txtPages.Text, Integer)
.CurrentPageIndex = 0
.DataBind()
End With
' відображається вид [résultats]
vueRésultats.Visible = True
vueFormulaire.Visible = False
vueErreurs.Visible = False
End Sub
Private Sub btnExécuter_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnExécuter.Click
' сторінка дійсна?
If Not Page.IsValid Then
afficheFormulaire()
Exit Sub
End If
' виконання запиту клієнта SELECT
Dim données As DataView
Try
données = CType(Application("objProduits"), produits).getDataSet(txtSelect.Text.Trim).Tables(0).DefaultView
Catch ex As Exception
Dim erreurs As New ArrayList
erreurs.Add("erreur d'accès à la base de données (" + ex.Message + ")")
afficheErreurs(erreurs, True)
Exit Sub
End Try
' все гаразд — відображаються результати
afficheRésultats(txtSelect.Text.Trim, données)
' дані заносяться в сесію
Session("données") = données
End Sub
Private Sub retourFormulaire(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkErreurs.Click, lnkRésultats.Click
' набір переглядів
vueErreurs.Visible = False
vueFormulaire.Visible = True
vueRésultats.Visible = False
End Sub
Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) Handles DataGrid1.PageIndexChanged
' перехід на іншу сторінку
With DataGrid1
.CurrentPageIndex = e.NewPageIndex
.DataSource = CType(Session("données"), DataView)
.DataBind()
End With
End Sub
Private Sub DataGrid1_SortCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles DataGrid1.SortCommand
' сортування dataview
Dim données As DataView = CType(Session("données"), DataView)
données.Sort = e.SortExpression + " " + CType(IIf(rdCroissant.Checked, "asc", "desc"), String)
' виводимо його
With DataGrid1
.CurrentPageIndex = 0
.DataSource = données
.DataBind()
End With
End Sub
End Class
Під час завантаження сторінки [Page_Load] ми спочатку перевіряємо, чи вдалося програмі правильно ініціалізуватися. Якщо ні, то відображається вікно [erreurs] без посилання на повернення до форми, оскільки це посилання в такому випадку є зайвим. Дійсно, якщо додаток не зміг правильно ініціалізуватися, може відображатися лише вікно [erreurs]. В іншому випадку, якщо це перший запит клієнта, відображається вікно [formulaire]. Щодо решти, ми залишаємо читачеві можливість самостійно розібратися в коді. Ми зупинимося лише на трьох процедурах: процедурі [btnExécuter_Click], яка виконується, коли користувач запросив виконання запиту SQL, введеного у вікні [formulaire], процедуру [DataGrid1_PageIndexChanged], яка виконується, коликористувач використовує посилання [Suivant] та [Précédent] з [DataGrid], а також процедура [DataGrid1_SortCommand], яка виконується, коли користувач клацає на заголовок стовпця, щоб відсортувати дані запорядку. Напрямок сортування — за зростанням чи за спаданням — визначається двома перемикачами сортування.
Отже, у процедурі [btnExécuter_Click] спочатку перевіряється, чи є сторінка валідною. Коли виконується процедура [btnExécuter_Click], перевірки, пов’язані з різними елементами валідації сторінки, вже виконано. Для кожного елемента валідації встановлено два атрибути:
приймає значення «true», якщо перевірені дані є валідними, і «false» — у протилежному випадку | |
повідомлення про помилку, якщо перевірені дані виявилися недійсними |
Для самої сторінки було встановлено атрибут [IsValid]. Він має значення «true» лише в тому випадку, якщо всі елементи перевірки мають атрибут [IsValid] із значенням «true». Якщо це не так, потрібно відобразити представлення [formulaire]. Воно містить елементи перевірки, які відображатимуть свій атрибут [errorMessage]. Якщо сторінка є валідною, використовується об’єкт типу [produits], створений [Application_Start], для отримання [DataSet], що відповідає виконанню запиту SQL select. Його перетворюють на об’єкт [DataView]:
Dim données As DataView
Try
données = CType(Application("objProduits"), produits).getDataSet(txtSelect.Text.Trim).Tables(0).DefaultView
...
Можна було б просто працювати з об’єктом [DataSet] і написати:
Dim données As DataSet
Try
données = CType(Application("objProduits"), produits).getDataSet(txtSelect.Text.Trim)
...
Об’єкт [DataSet] — це, по суті, сукупність таблиць, пов’язаних між собою відношеннями. У нашому конкретному додатку об’єкт [DataSet], отриманий із класу [produits], містить лише одну таблицю — ту, що є результатом виконання інструкції [select]. Таблицю можна сортувати, тоді як [DataSet] — ні, але нас цікавить саме сортування отриманих даних. Щоб працювати з таблицею, отриманою в результаті виконання [select], можна було б написати:
Dim données As DataTable
Try
données = CType(Application("objProduits"), produits).getDataSet(txtSelect.Text.Trim).Tables(0)
...
Об’єкт [DataTable], хоча й представляє таблицю бази даних, не має методу сортування. Для цього потрібно створити представлення таблиці. Представлення — це об’єкт типу [DataView]. За допомогою фільтрів можна створити різні представлення однієї й тієї ж таблиці. Таблиця має подання за замовчуванням, у якому не визначено жодного фільтра. Отже, воно відображає всю таблицю. Це подання за замовчуванням отримується за допомогою [DataTable.DefaultView]. Подання можна сортувати за допомогою його властивості [sort], до якої ми ще повернемося.
Якщо отримання [DataSet] з класу [produits] проходить успішно, відображається представлення [résultats], інакше — представлення [erreurs]. Відображення подання [résultats] здійснюється за допомогою процедури [afficheRésultats], якій передаються два параметри:
- текст, який потрібно розмістити в мітці [lblSelect]
- [DataView], який потрібно пов’язати з [DataGrid1]
Цей приклад демонструє високу гнучкість компонента [DataGrid]. Він здатний розпізнавати структуру [DataView], з яким його пов’язують, та адаптуватися до неї. Нарешті, процедура [btnExécuter_Click] зберігає щойно отриманий [DataView] у сесії користувача, щоб мати його під рукою, коли користувач запитуватиме інші сторінки з того самого [DataView].
Процедура [DataGrid1_PageIndexChanged] виконується, коли користувач використовує посилання [Suivant] та [Précédent] з [DataGrid]. Вона отримує два параметри:
Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.) Handles DataGrid1.PageIndexChanged
об’єкт, що ініціював подію — у даному випадку одне з посилань [Suivant] або [Précédent] | |
інформація про подію. Атрибут e.NewPageIndex — це номер сторінки, яку потрібно відобразити у відповідь на запит клієнта |
Повний код обробника події виглядає так:
Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) Handles DataGrid1.PageIndexChanged
' перехід на іншу сторінку
With DataGrid1
.CurrentPageIndex = e.NewPageIndex
.DataSource = CType(Session("données"), DataView)
.DataBind()
End With
End Sub
Компонент [DataGrid] має атрибут [CurrentPageIndex], який вказує номер сторінки, яку він відображає або збирається відобразити. Цьому атрибуту присвоюється значення [NewPageIndex] з параметра [e]. Потім [DataGrid] пов’язується з [DataView], який було збережено в сесії за допомогою процедури [btnExécuter_Click].
Можна задатися питанням, чи потрібен атрибут [EnableViewState=true] для [DataGrid], оскільки його вміст обчислюється кодом під час кожного нового виконання сторінки. Можна було б подумати, що ні. Однак, якщо [DataGrid] має атрибут [EnableViewState=false], то можна помітити, що подія [DataGrid1.PageIndexChanged] ніколи не спрацьовує. Саме тому ми залишили [EnableViewState=true]. Відомо, що це призводить до того, що вміст [DataGrid] буде розміщено у прихованому полі [__VIEWSTATE] на сторінці. Це може призвести до значного перевантаження сторінки, якщо [DataGrid] є великим. Якщо це створює незручності, можна самостійно керувати пагінацією, не використовуючи автоматичну пагінацію [DataGrid].
Процедура [DataGrid1_SortCommand] виконується, коли користувач клацає на заголовку одного зі стовпців, що відображаються [DataGrid], щоб сортувати дані за цим стовпцем. Вона отримує два параметри:
Private Sub DataGrid1_SortCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles DataGrid1.SortCommand
об’єкт, що ініціював подію — у даному випадку одне з посилань [Suivant] або [Précédent] | |
інформація про подію. Атрибут [e.SortExpression] — це назва стовпця, на який натиснули для сортування |
Повний код обробника події виглядає так:
Private Sub DataGrid1_SortCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles DataGrid1.SortCommand
' сортування dataview
Dim données As DataView = CType(Session("données"), DataView)
données.Sort = e.SortExpression + " " + CType(IIf(rdCroissant.Checked, "asc", "desc"), String)
' виводиться
With DataGrid1
.CurrentPageIndex = 0
.DataSource = données
.DataBind()
End With
End Sub
З поточної сесії витягується [DataView], який відображається [DataGrid]. Його туди помістила процедура [btnExécuter_Click]. Компонент [DataView] має властивість [Sort], якій присвоюється вираз сортування. Вона відповідає синтаксису [select ... order by expr1, expr2, ...], де за кожним [expri] може слідувати ключове слово [asc] для сортування за зростанням або [desc] для сортування за спаданням. Вираз [order by], що використовується тут, — це [order by colonne asc/desc]. Властивість [e.SortExpression] надає нам назву стовпця [DataGrid], на який було натиснуто для сортування. Рядок [asc/desc] визначається на основі значень перемикачів групи [rdTri]. Після визначення виразу сортування для [DataView], [DataGrid] пов’язується з ним. [DataGrid] розміщується на його першій сторінці.
8.7. Компонент DataList та зв’язок даних
Тепер розглянемо компонент [DataList]. Він пропонує більше можливостей форматування, ніж [DataGrid], але є менш гнучким. Так, він не може автоматично адаптуватися до джерела даних, з яким пов’язаний. Якщо така адаптація потрібна, її слід здійснити за допомогою коду. Якщо структура джерела даних відома заздалегідь, то цей компонент пропонує такі можливості форматування, що його можна вважати кращим за [DataGrid].
8.7.1. Застосування
Щоб проілюструвати використання [DataList], ми створюємо додаток MVC, аналогічний попередньому:
![]() |
Ці три види будуть вбудовані в код представлення контролера [main.aspx] у вигляді контейнерів. Отже, цей додаток має єдину сторінку [main.aspx].
8.7.2. Бізнес-класи
Клас [produits] є таким самим, як і раніше.
8.7.3. Види
Коли користувач вперше звертається до додатка, він отримує такий вигляд [résultats1]:
![]() |
№ | назва | тип | властивості | роль |
1 | RadioButton | EnableViewState=false | дозволяє вибрати один із двох стилів [DataList] | |
2 | Button | EnableViewState=false | кнопка [submit] | |
3 | DataList | EnableViewState=true | поле відображення списку даних |
Якщо користувач вибере стиль № 2, він отримає такий вигляд: [résultats2]:
![]() |
№ | назва | тип | властивості | роль |
1 | DataList | EnableViewState=true | поле відображення списку даних |
Вигляд [erreurs] повідомляє про проблему з доступом до джерела даних:
![]() |
№ | назва | тип | властивості | роль |
1 | змінна | код HTML, необхідний для відображення помилок |
Три види додатка — це три різні контейнери (панелі) на одній сторінці [main.aspx]. Її код відображення такий:
<%@ Page src="main.aspx.vb" inherits="main" autoeventwireup="false" Language="vb" %>
<HTML>
<HEAD>
</HEAD>
<body>
<P>Liaison de données avec un DataList</P>
<HR width="100%" SIZE="1">
<form runat="server" ID="Form1">
<asp:Panel Runat="server" ID="bandeau">
<P>Choisissez votre style :
<asp:RadioButton id="RadioButton1" runat="server" EnableViewState="False" Text="1" GroupName="rdstyle"
Checked="True"></asp:RadioButton>
<asp:RadioButton id="RadioButton2" runat="server" EnableViewState="False" Text="2" GroupName="rdstyle"></asp:RadioButton>
<asp:Button id="btnChanger" runat="server" EnableViewState="False" Text="Changer"></asp:Button></P>
<HR width="100%" SIZE="1">
</asp:Panel>
<asp:Panel id="vueRésultats1" runat="server">
<P>
<asp:DataList id="DataList1" runat="server" BorderColor="Tan" BorderWidth="1px" BackColor="LightGoldenrodYellow"
CellPadding="2" RepeatDirection="Horizontal" RepeatColumns="4" ForeColor="Black">
<SelectedItemStyle ForeColor="GhostWhite" BackColor="DarkSlateBlue"></SelectedItemStyle>
<HeaderTemplate>
Contenu de la table [liste] de la base [produits]
</HeaderTemplate>
<AlternatingItemStyle BackColor="PaleGoldenrod"></AlternatingItemStyle>
<ItemTemplate>
nom :
<%# Container.DataItem("ім'я") %>
<br>
prix :
<%# databinder.eval(Container.DataItem,"ціна","{0:C}") %>
<br>
</ItemTemplate>
<FooterStyle BackColor="Tan"></FooterStyle>
<HeaderStyle Font-Bold="True" BackColor="Tan"></HeaderStyle>
</asp:DataList></P>
</asp:Panel>
<asp:Panel id="vueRésultats2" runat="server">
<P>
<asp:DataList id="DataList2" runat="server">
<HeaderTemplate>
Contenu de la table [liste] de la base [produits]
<HR width="100%" SIZE="1">
</HeaderTemplate>
<AlternatingItemStyle BackColor="Teal"></AlternatingItemStyle>
<SeparatorStyle BackColor="LightSkyBlue"></SeparatorStyle>
<ItemStyle BackColor="#C0C000"></ItemStyle>
<ItemTemplate>
nom :
<%# Container.DataItem("назва") %>
, prix :
<%# databinder.eval(Container.DataItem,"ціна","{0:C}") %>
<BR>
</ItemTemplate>
<SeparatorTemplate>
<HR width="100%" SIZE="1">
</SeparatorTemplate>
<HeaderStyle BackColor="#C0C0FF"></HeaderStyle>
</asp:DataList></P>
</asp:Panel>
<asp:Panel id="vueErreurs" runat="server">
<P>Les erreurs suivantes se sont produites :</P>
<%= erreursHTML %>
</asp:Panel>
</form>
</body>
</HTML>
8.7.4. Налаштування компонентів [DataList]
Розглянемо різні атрибути компонента [DataList]. Їх дуже багато, і ми наведемо лише невелику частину. Усередині [DataList] можна визначити до семи шаблонів відображення:
шаблон заголовка [DataList] | |
шаблон рядків, що відображають елементи пов'язаного списку даних. Обов'язковим є лише цей шаблон. | |
щоб візуально розрізнити послідовні елементи, що відображаються, можна використовувати два шаблони: ItemTemplate для елемента n, AlternatingItemTemplate для елемента n+1 | |
шаблон вибраного елемента в [DataList] | |
шаблон розділювача між двома елементами у [DataList] | |
[DataList] дозволяє змінювати значення, які він відображає. [EditItemTemplate] — це шаблон елемента з [DataList], для якого активний режим «редагування» | |
шаблон нижнього колонтитулу для [DataList] |
Компонент [DataList1] було створено на основі [WebMatrix]. У його вікні властивостей було вибрано посилання [Mise en forme automatique]:
![]() | 1234567 ![]() |
Як показано вище, схема [Couleur 5] згенерує [DataList] зі стилями для таких шаблонів: HeaderTemplate (1), ItemTemplate (2, 6), AlternatingTemplate (3, 5), SelectedItemTemplate (4), FooterTemplate (7). Згенерований код має такий вигляд:
<asp:DataList id="DataList1" runat="server" BorderColor="Tan" BorderWidth="1px" BackColor="LightGoldenrodYellow"
CellPadding="2" ForeColor="Black">
<SelectedItemStyle ForeColor="GhostWhite" BackColor="DarkSlateBlue"></SelectedItemStyle>
<AlternatingItemStyle BackColor="PaleGoldenrod"></AlternatingItemStyle>
<FooterStyle BackColor="Tan"></FooterStyle>
<HeaderStyle Font-Bold="True" BackColor="Tan"></HeaderStyle>
</asp:DataList></P>
Шаблон не визначено. Це ми маємо зробити. Ми визначаємо такі шаблони:
| |
|
Нагадаємо, що модель [ItemTemplate] відповідає відображенню елементів джерела даних, пов'язаного з [DataList]. Це джерело даних — це набір рядків даних, кожен з яких містить одне або кілька значень. Поточний рядок джерела даних представлений об’єктом [Container.DataItem]. Такий рядок має стовпці. [Container.DataItem("col1")] — це значення стовпця «col1» поточного рядка. Щоб включити це значення в код відображення, потрібно написати <%# Container.DataItem("col") %>. Іноді потрібно відобразити елемент поточного рядка у спеціальному форматі. У цьому випадку потрібно відобразити стовпець «ціна» поточного рядка в євро. Для цього використовується функція [DataBinder.Eval], яка приймає три параметри:
- поточний рядок [Container.DataItem]
- назву стовпця, який потрібно відформатувати
- рядок форматування у вигляді {0:формат}, де [format] — один із форматів, що підтримуються методом [string.format].
Таким чином, код <%# DataBinder.Eval(Container.DataItem,"ціна",{0:C}) %> відобразить стовпець [prix] поточного рядка у грошовому форматі (формат C = Currency).
Отже, ми отримаємо [DataList], який виглядатиме так:

У наведеному вище прикладі дані розміщено по чотири записи в рядку. Це досягається за допомогою таких атрибутів [DataList]:
Горизонтально | |
бажана кількість стовпців |
У підсумку код [DataList1] є тим, що було представлено у коді відображення трохи вище. Ми залишаємо читачеві можливість самостійно вивчити код відображення [DataList2]. Як і у випадку з компонентом [DataGrid], більшість властивостей [DataList] можна налаштувати за допомогою майстра [WebMatrix]. Для цього використовується посилання [Générateur de propriétés] у вікні властивостей [DataList]:
![]() | ![]() |
8.7.5. Елементи управління
Контролер [global.asax, global.asax.vb] має такий вигляд:
[global.asax]
[global.asax.vb]
Imports System
Imports System.Web
Imports System.Web.SessionState
Imports st.istia.univangers.fr
Imports System.Configuration
Imports System.Data
Imports System.Collections
Public Class Global
Inherits System.Web.HttpApplication
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
' створюємо об’єкт «продукти»
Try
Dim données As DataSet = New produits(ConfigurationSettings.AppSettings("OLEDBStringConnection")).getDataSet("select * from LISTE")
' додаємо об’єкт до додатка
Application("données") = données
' помилок немає
Application("erreur") = False
Catch ex As Exception
'сталася помилка, її фіксують у додатку
Application("erreur") = True
Application("message") = ex.Message
End Try
End Sub
End Class
Під час запуску додатка (Application_Start) ми створюємо об’єкт [dataset] на основі бізнес-класу [produits] і розміщуємо його в додатку, щоб він був доступний для всіх запитів усіх клієнтів. Якщо під час цього створення виникає виняток, він фіксується в додатку. Процедура [Application_Start] виконуватиметься лише один раз. Після цього контролер [global.asax] більше не буде задіяний. Цю роботу виконуватиме контролер [main.aspx.vb]:
Imports System.Collections
Imports Microsoft.VisualBasic
Imports System.Data
Imports st.istia.univangers.fr
Imports System
Imports System.Xml
Public Class main
Inherits System.Web.UI.Page
' компоненти сторінки
Protected WithEvents vueErreurs As System.Web.UI.WebControls.Panel
Protected WithEvents DataList1 As System.Web.UI.WebControls.DataList
Protected WithEvents DataList2 As System.Web.UI.WebControls.DataList
Protected WithEvents vueRésultats1 As System.Web.UI.WebControls.Panel
Protected WithEvents vueRésultats2 As System.Web.UI.WebControls.Panel
Protected WithEvents RadioButton1 As System.Web.UI.WebControls.RadioButton
Protected WithEvents RadioButton2 As System.Web.UI.WebControls.RadioButton
Protected WithEvents btnChanger As System.Web.UI.WebControls.Button
Protected WithEvents bandeau As System.Web.UI.WebControls.Panel
' дані сторінки
Protected erreursHTML As String
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' перевіряємо, чи є помилка в додатку
If CType(Application("erreur"), Boolean) Then
' додаток не ініціалізувався належним чином
Dim erreurs As New ArrayList
erreurs.Add("Application momentanément indisponible (" + CType(Application("message"), String) + ")")
afficheErreurs(erreurs, False)
Exit Sub
End If
'перший запит
If Not IsPostBack Then
' ініціалізуються елементи управління
With DataList1
.DataSource = CType(Application("données"), DataSet)
.DataBind()
End With
With DataList2
.DataSource = CType(Application("données"), DataSet)
.DataBind()
End With
' відображається порожня форма
afficheRésultats(True, False)
End If
End Sub
Private Sub afficheErreurs(ByVal erreurs As ArrayList, ByVal afficheLien As Boolean)
' відображається вікно помилок
erreursHTML = ""
For i As Integer = 0 To erreurs.Count - 1
erreursHTML += "<li>" + erreurs(i).ToString + "</li>" + ControlChars.CrLf
Next
' відображається вигляд [erreurs]
vueErreurs.Visible = True
vueRésultats1.Visible = False
vueRésultats2.Visible = False
bandeau.Visible = False
End Sub
Private Sub afficheRésultats(ByVal visible1 As Boolean, ByVal visible2 As Boolean)
' відображається вигляд [résultats]
vueRésultats1.Visible = visible1
vueRésultats2.Visible = visible2
vueErreurs.Visible = False
End Sub
Private Sub btnChanger_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnChanger.Click
' змінюється стиль
afficheRésultats(RadioButton1.Checked, RadioButton2.Checked)
End Sub
End Class
8.8. Компонент Repeater та зв'язок даних
Компонент [Repeater] дозволяє повторити код HTML для кожного елемента списку даних. Припустимо, що потрібно відобразити список помилок у такому вигляді:

Ми вже стикалися з цією проблемою і вирішили її, вставивши в код відображення змінну у вигляді <ul><% =erreursHTML %></ul>, причому значення erreursHTML обчислюється контролером. Це значення містить код HTML, що відповідає списку. Недоліком такого підходу є те, що для зміни оформлення цього списку HTML доводиться звертатися до частини контролера, що суперечить принципу розділення контролера та представлення. Компонент [Repeater] пропонує нам рішення. Як і у випадку з [DataList], можна визначити шаблони <HeaderTemplate> для заголовка, <ItemTemplate> для поточного елемента списку даних та <FooterTemplate> для кінця даних. У цьому випадку ми можемо мати таке визначення для компонента [Repeater]:
<asp:Repeater id="Repeater1" runat="server" EnableViewState="False">
<HeaderTemplate>
Les erreurs suivantes se sont produites :
<ul>
</HeaderTemplate>
<ItemTemplate>
<li>
<%# Container.DataItem %>
</li>
</ItemTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
Нагадаємо, що [Container.DataItem] представляє рядок даних, якщо джерело даних має кілька стовпців. Воно представляє одне значення, якщо джерело має лише один стовпець. Саме так і буде в даному випадку. Для прикладу, ми створюємо такий додаток:

Код для відображення сторінки такий:
<html>
<head>
</head>
<body>
<form runat="server">
<p>
Liaison de données avec un composant [Repeater]
</p>
<hr />
<p>
<asp:Repeater id="Repeater1" runat="server" EnableViewState="False">
<ItemTemplate>
<li>
<%# Container.DataItem %>
</li>
</ItemTemplate>
<HeaderTemplate>
Les erreurs suivantes se sont produites :
<ul>
</HeaderTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
</p>
</form>
</body>
</html>
Контрольний код такий:
<%@ Page Language="VB" %>
<script runat="server">
' процедура, що виконується під час завантаження сторінки
Sub page_Load(sender As Object, e As EventArgs)
if not IsPostBack then
' створюється джерело даних
with Repeater1
.DataSource=createDataSource
.DataBind
end with
end if
End Sub
function createDataSource as ArrayList
' створюється масив-список
dim erreurs as new ArrayList
dim i as integer
for i=0 to 5
erreurs.add("erreur-"+i.ToString)
next
return erreurs
end function
</script>
<html>
...
</html>
На перший запит клієнта ми пов'язуємо об'єкт [ArrayList] з компонентом [Repeater], який, як передбачається, представляє список помилок.
8.9. Application
Тут ми повертаємося до додатка, який вже розглядався з серверними компонентами. Додаток дозволяє проводити симуляції розрахунків податків. Він базується на класі [impot], про який ми тут не будемо згадувати. Цей клас потребує даних, які він отримує з джерела даних OLEDB. Для прикладу це буде джерело ACCESS. У цій новій версії ми впроваджуємо такі нововведення:
- використання компонентів валідації для перевірки правильності даних
- використання серверних компонентів, пов’язаних із джерелами даних, для відображення результатів
8.9.1. Структура MVC додатка
Структура MVC додатка виглядає наступним чином:
![]() |
Ці три подання будуть вбудовані в код представлення контролера [main.aspx] у вигляді контейнерів. Отже, цей додаток має єдину сторінку [main.aspx].
8.9.2. Види додатка
Вигляд [formulaire] — це форма введення даних, що дозволяє розрахувати податок для користувача:

Користувач заповнює форму:

Він використовує кнопку [Envoyer], щоб ініціювати розрахунок свого податку. Він отримує наступний екран [simulations]:

Він повертається до форми за посиланням вище. Форма відображається в тому стані, в якому він її заповнив. Він може припуститися помилок під час введення даних:

Про них йому повідомляється на екрані [formulaire]:

Користувач виправляє свої помилки. Він може виконати нові симуляції:

У результаті він отримує вікно [simulations] із ще одним моделюванням:

Нарешті, якщо джерело даних недоступне, про це повідомляється користувачеві у вікні [erreurs]:

8.9.2.1. Код відображення
Нагадаємо, що сторінка [main.aspx] об’єднує всі перегляди. Це єдина форма з трьома контейнерами:
- [panelform] для подання [formulaire]
- [panelerreurs] для подання [erreurs]
- [panelsimulations] для подання [simulations]
Тепер детально розглянемо компоненти цих трьох контейнерів. Контейнер [panelform] має таке візуальне представлення:
![]() |
№ | назва | тип | властивості | роль |
Панель | вигляд форми | |||
RadioButton | GroupName=rdmarie | перемикачі | ||
CustomValidator | ErrorMessage=Ви не вказали свій сімейний стан EnableClientScript=false | перевіряє, чи клієнтська програма надіслала очікуваний сімейний стан | ||
TextBox | кількість дітей | |||
RequiredFieldValidator | ErrorMessage=Вкажіть кількість дітей ControlToValidate=txtEnfants | перевіряє, чи поле [txtEnfants] не порожнє | ||
RangeValidator | ErrorMessage=Введіть число від 1 до 30 ControlToValidate=txtEnfants | перевіряє, чи поле [txtEnfants] знаходиться в діапазоні [1,30] | ||
TextBox | EnableViewState=true | річний оклад | ||
RequiredFieldValidator | ControlToValidate=txtSalaire ErrorMessage=Вкажіть суму своєї заробітної плати | перевіряє, чи поле [txtSalaire] не порожнє | ||
RegularExpressionValidator | ControlToValidate=txtSalaire ErrorMessage=Неправильна заробітна плата RegularExpression=\s*\d+\s* | перевіряє, чи поле [txtSalaire] є послідовністю цифр | ||
Button | CausesValidation=true | кнопка [submit] у формі — запускає розрахунок податку | ||
Кнопка | CausesValidation=false | кнопка [submit] у формі — очищає форму |
Може викликати подив елемент контролю [cvMarié], який відповідає за перевірку того, чи користувач дійсно встановив прапорець на одному з двох перемикачів. Адже інакше бути не може, якщо він використовує саме ту форму, яку надіслав сервер. Оскільки ніщо не може цього гарантувати, доводиться перевіряти всі надіслані параметри. Також слід звернути увагу на атрибут [CausesValidation=false] кнопки [btnEffacer]. Адже коли користувач натискає цю кнопку, відправлені дані перевіряти не потрібно, оскільки вони будуть проігноровані.
Усі компоненти контейнера мають властивість [EnableViewState=false], крім [panelForm, txtEnfants, txtSalaire, rdOui, rdNon]. Контейнер [panelerreurs] має таке візуальне представлення:
![]() |
№ | назва | тип | властивості | роль |
Панель | EnableViewState=false | перегляд помилок | ||
Повторювач | EnableViewState=false | відображає список помилок |
Компонент [rptErreurs] визначено таким чином:
<asp:Repeater id="rptErreurs" runat="server" EnableViewState="False">
<ItemTemplate>
<li>
<%# Container.Dataitem%>
</li>
</ItemTemplate>
<HeaderTemplate>
Les erreurs suivantes se sont produites
<ul>
</HeaderTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
Список даних, пов'язаний з компонентом [rptErreurs], буде об'єктом [ArrayList], що містить список повідомлень про помилки. Отже, у наведеному вище прикладі <%# Container.Dataitem%> позначає поточне повідомлення про помилку. Контейнер [panelsimulations] має таке візуальне представлення:
![]() |
№ | назва | тип | властивості | роль |
Панель | EnableViewState=false | перегляд симуляцій | ||
LinkButton | EnableViewState=false | посилання на форму | ||
DataGrid | EnableViewState=false | відповідає за відображення симуляцій, розміщених в об’єкті [DataTable] |
Компонент [dgSimulations] налаштований наступним чином у [Webmatrix]. У вікні властивостей [dgSimulations] ми вибираємо посилання [Mise en forme automatique] і вибираємо схему [Couleur 3]:
![]() | ![]() |
Потім, не виходячи з вікна властивостей [dlgSimulations], вибираємо посилання [Générateur de propriétés]. Вимагаємо відображення заголовків стовпців:

Вибираємо опцію [Colonnes], щоб визначити чотири стовпці [DataGrid]:

Знімаємо галочку з рядка [Créer des colonnes automatiquement ...]. Ми самостійно визначимо чотири стовпці для [DataGrid]. Цей об’єкт буде пов’язаний з об’єктом [DataTable], що має чотири стовпці з назвами «одружений», «діти», «зарплата» та «податок». Щоб створити стовпець у [DataGrid], ми вибираємо опцію [Colonnes connexe] і використовуємо кнопку створення, як показано вище. Стовпець створено, і ми можемо визначити його характеристики. Перший стовпець таблиці моделювання міститиме стовпець «одружений» об’єкта [DataTable], який буде пов’язаний з [DataGrid]. Цей перший стовпець [DataGrid] визначається в майстрі наступним чином:

заголовок стовпця, у даному випадку «Одружений» | |
назва стовпця джерела даних, який буде відображатися цим стовпцем [DataGrid]. У даному випадку це стовпець «одружений» з [DataTable]. |
Другий стовпець визначається наступним чином:
Діти | |
діти |
Третій стовпець визначається наступним чином:
Річний оклад | |
зарплата | |
{0:C} — форматування валюти для відображення знака євро |
Четвертий стовпець визначається наступним чином:
Сума податку | |
податок | |
{0:C} — грошове відображення для отримання знака євро |
Ми розміщуємо код форматування та код контролю у двох окремих файлах. Перший буде у файлі [main.aspx], а другий — у файлі [main.aspx.vb]. Код [main.aspx] виглядає так:
<%@ page src="main.aspx.vb" inherits="main" AutoEventWireUp="false" %>
<HTML>
<HEAD>
<title>Calculer votre impôt</title>
</HEAD>
<body>
<P>Calculer votre impôt</P>
<HR width="100%" SIZE="1">
<FORM id="Form1" runat="server">
<asp:panel id="panelform" Runat="server">
<TABLE id="Table1" cellSpacing="1" cellPadding="1" border="0">
<TR>
<TD height="19">Etes-vous marié(e)</TD>
<TD height="19">
<asp:RadioButton id="rdOui" runat="server" GroupName="rdMarie"></asp:RadioButton>Oui
<asp:RadioButton id="rdNon" runat="server" GroupName="rdMarie" Checked="True"></asp:RadioButton>Non
<asp:CustomValidator id="cvMarie" runat="server" ErrorMessage="Vous n'avez pas indiqué votre état marital"
Display="Dynamic" EnableClientScript="False" Visible="False" EnableViewState="False"></asp:CustomValidator></TD>
</TR>
<TR>
<TD>Nombre d'enfants</TD>
<TD>
<asp:TextBox id="txtEnfants" runat="server" Columns="3" MaxLength="3"></asp:TextBox>
<asp:RequiredFieldValidator id="rfvEnfants" runat="server" ErrorMessage="Indiquez le nombre d'enfants" Display="Dynamic"
ControlToValidate="txtEnfants" EnableViewState="False"></asp:RequiredFieldValidator>
<asp:RangeValidator id="rvEnfants" runat="server" ErrorMessage="Tapez un nombre entre 1 et 30" Display="Dynamic"
ControlToValidate="txtEnfants" Type="Integer" MaximumValue="30" MinimumValue="0" EnableViewState="False"></asp:RangeValidator></TD>
</TR>
<TR>
<TD>Salaire annuel (euro)</TD>
<TD>
<asp:TextBox id="txtSalaire" runat="server" Columns="10" MaxLength="10"></asp:TextBox>
<asp:RequiredFieldValidator id="rfvSalaire" runat="server" ErrorMessage="Indiquez le montant de votre salaire"
Display="Dynamic" ControlToValidate="txtSalaire" EnableViewState="False"></asp:RequiredFieldValidator>
<asp:RegularExpressionValidator id="revSalaire" runat="server" ErrorMessage="Salaire invalide" Display="Dynamic"
ControlToValidate="txtSalaire" ValidationExpression="\s*\d+\s*" EnableViewState="False"></asp:RegularExpressionValidator></TD>
</TR>
</TABLE>
<P>
<asp:Button id="btnCalculer" runat="server" Text="Calculer"></asp:Button>
<asp:Button id="btnEffacer" runat="server" Text="Effacer" CausesValidation="False"></asp:Button></P>
</asp:panel>
<asp:panel id="panelerreurs" runat="server" EnableViewState="False">
<asp:Repeater id="rptErreurs" runat="server" EnableViewState="False">
<ItemTemplate>
<li>
<%# Container.Dataitem%>
</li>
</ItemTemplate>
<HeaderTemplate>
Les erreurs suivantes se sont produites
<ul>
</HeaderTemplate>
<FooterTemplate>
</ul>
</FooterTemplate>
</asp:Repeater>
</asp:panel>
<asp:panel id="panelsimulations" runat="server" EnableViewState="False">
<P>
<asp:DataGrid id="dgSimulations" runat="server" BorderColor="#DEBA84" BorderStyle="None" CellSpacing="2"
BorderWidth="1px" BackColor="#DEBA84" CellPadding="3" AutoGenerateColumns="False" EnableViewState="False">
<SelectedItemStyle Font-Bold="True" ForeColor="White" BackColor="#738A9C"></SelectedItemStyle>
<ItemStyle ForeColor="#8C4510" BackColor="#FFF7E7"></ItemStyle>
<HeaderStyle Font-Bold="True" ForeColor="White" BackColor="#A55129"></HeaderStyle>
<FooterStyle ForeColor="#8C4510" BackColor="#F7DFB5"></FooterStyle>
<Columns>
<asp:BoundColumn DataField="marié" HeaderText="Marié"></asp:BoundColumn>
<asp:BoundColumn DataField="enfants" HeaderText="Enfants"></asp:BoundColumn>
<asp:BoundColumn DataField="salaire" HeaderText="Salaire annuel" DataFormatString="{0:C}"></asp:BoundColumn>
<asp:BoundColumn DataField="impôt" HeaderText="Montant de l'impôt" DataFormatString="{0:C}"></asp:BoundColumn>
</Columns>
<PagerStyle HorizontalAlign="Center" ForeColor="#8C4510" Mode="NumericPages"></PagerStyle>
</asp:DataGrid></P>
<P>
<asp:LinkButton id="lnkForm2" runat="server" EnableViewState="False">Retour au formulaire</asp:LinkButton></P>
</asp:panel>
</FORM>
</body>
</HTML>
8.9.3. Контрольний код додатка
Код контролю додатка розміщений у файлах [global.asax.vb] та [main.aspx.vb]. Файл [global.asax] має такий вигляд:
Файл [global.asax.vb] має такий вигляд:
Imports System
Imports System.Web
Imports System.Web.SessionState
Imports st.istia.univangers.fr
Imports System.Configuration
Imports System.Collections
Imports System.Data
Public Class Global
Inherits System.Web.HttpApplication
Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
' створюємо об’єкт «impot»
Dim objImpot As impot
Try
objImpot = New impot(New impotsOLEDB(ConfigurationSettings.AppSettings("chaineConnexion")))
' додаємо об’єкт до програми
Application("objImpot") = objImpot
' помилок немає
Application("erreur") = False
Catch ex As Exception
'сталася помилка, її фіксують у додатку
Application("erreur") = True
Application("message") = ex.Message
End Try
End Sub
Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
' початок сеансу — створюється порожній список симуляцій
Dim simulations As New DataTable("simulations")
simulations.Columns.Add("marié", Type.GetType("System.String"))
simulations.Columns.Add("enfants", Type.GetType("System.String"))
simulations.Columns.Add("salaire", Type.GetType("System.Int32"))
simulations.Columns.Add("impôt", Type.GetType("System.Int32"))
Session.Item("simulations") = simulations
End Sub
End Class
Під час запуску програми (перший запит до програми) виконується процедура [Application_Start]. Вона намагається створити об’єкт типу [impot], беручи дані з джерела OLEDB. Якщо читач забув, де саме було визначено цей клас, йому слід ознайомитися з розділом 5. Створення об’єкта [impot] може завершитися невдачею, якщо джерело даних недоступне. У цьому випадку помилка зберігається в додатку, щоб усі наступні запити знали, що цей об’єкт не вдалося правильно ініціалізувати. Якщо створення проходить успішно, створений об’єкт [impot] також зберігається в додатку. Він використовуватиметься всіма запитами на розрахунок податку. Коли клієнт надсилає свій перший запит, процедура [Application_Start] створює для нього сесію. Ця сесія призначена для збереження різних моделювань розрахунку податку, які він буде виконувати. Вони будуть збережені в об’єкті [DataTable], пов’язаному з ключем сесії «simulations». Під час запуску сесії цей ключ пов’язується з порожнім об’єктом [DataTable], структура якого вже визначена. Інформація, необхідна для роботи програми, розміщується в її конфігураційному файлі [wenConfig]:
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<appSettings>
<add key="chaineConnexion" value="Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=D:\data\devel\aspnet\poly\webforms2\vs\impots6\impots.mdb" />
</appSettings>
</configuration>
Ключ [chaineConnexion] позначає ланцюг підключення до джерела OLEDB. Інша частина контрольного коду міститься в [main.aspx.vb]:
Imports System.Collections
Imports Microsoft.VisualBasic
Imports st.istia.univangers.fr
Imports System
Imports System.Web.UI.Control
Imports System.Data
Public Class main
Inherits System.Web.UI.Page
Protected WithEvents rdOui As System.Web.UI.WebControls.RadioButton
Protected WithEvents rdNon As System.Web.UI.WebControls.RadioButton
Protected WithEvents txtEnfants As System.Web.UI.WebControls.TextBox
Protected WithEvents txtSalaire As System.Web.UI.WebControls.TextBox
Protected WithEvents btnCalculer As System.Web.UI.WebControls.Button
Protected WithEvents btnEffacer As System.Web.UI.WebControls.Button
Protected WithEvents panelform As System.Web.UI.WebControls.Panel
Protected WithEvents lnkForm2 As System.Web.UI.WebControls.LinkButton
Protected WithEvents panelerreurs As System.Web.UI.WebControls.Panel
Protected WithEvents rfvEnfants As System.Web.UI.WebControls.RequiredFieldValidator
Protected WithEvents rvEnfants As System.Web.UI.WebControls.RangeValidator
Protected WithEvents rfvSalaire As System.Web.UI.WebControls.RequiredFieldValidator
Protected WithEvents rptErreurs As System.Web.UI.WebControls.Repeater
Protected WithEvents dgSimulations As System.Web.UI.WebControls.DataGrid
Protected WithEvents revSalaire As System.Web.UI.WebControls.RegularExpressionValidator
Protected WithEvents cvMarie As System.Web.UI.WebControls.CustomValidator
Protected WithEvents panelsimulations As System.Web.UI.WebControls.Panel
' локальні змінні
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
...
End Sub
Private Sub afficheErreurs(ByRef erreurs As ArrayList)
...
End Sub
Private Sub afficheFormulaire()
...
End Sub
Private Sub afficheSimulations(ByRef simulations As DataTable, ByRef lien As String)
...
End Sub
Private Sub btnCalculer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCalculer.Click
....
End Sub
Private Sub lnkForm1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs)
...
End Sub
Private Sub lnkForm2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm2.Click
...
End Sub
Private Sub btnEffacer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEffacer.Click
...
End Sub
Private Sub razForm()
...
End Sub
Private Sub cvMarie_ServerValidate(ByVal source As System.Object, ByVal args As System.Web.UI.WebControls.ServerValidateEventArgs)
...
End Sub
End Class
Перша подія, яку обробляє код, — це [Page_Load]:
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
' спочатку перевіряємо стан програми
If CType(Application("erreur"), Boolean) Then
' додаток не зміг ініціалізуватися
' виводимо вікно помилок
Dim erreurs As New ArrayList
erreurs.Add("Application momentanément indisponible (" + CType(Application("message"), String) + ")")
afficheErreurs(erreurs)
Exit Sub
End If
' помилок немає — при першому запиті відображаємо форму
If Not IsPostBack Then afficheFormulaire()
End Sub
Перш ніж розпочати обробку запиту, ми переконуємося, що додаток вдалося правильно ініціалізувати. Якщо це не так, відображається вікно [erreurs] за допомогою процедури [afficheErreurs]. Якщо це перший запит (IsPostBack=false), ми відображаємо подання [formulaire] за допомогою [afficheFormulaire].
Процедура, що відображає подання [erreurs], виглядає наступним чином:
Private Sub afficheErreurs(ByRef erreurs As ArrayList)
' складається список помилок
With rptErreurs
.DataSource = erreurs
.DataBind()
End With
' відображення контейнерів
panelerreurs.Visible = True
panelform.Visible = False
panelsimulations.Visible = False
Exit Sub
End Sub
Процедура отримує як параметр список повідомлень про помилки в [erreurs] типу [ArrayList]. Ми просто пов'язуємо це джерело даних із компонентом [rptErreurs], який відповідає за його відображення.
Процедура, що відображає представлення [formulaire], має такий вигляд:
Private Sub afficheFormulaire()
' відображає форму
panelform.Visible = True
' інші контейнери приховані
panelerreurs.Visible = False
panelsimulations.Visible = False
End Sub
Ця процедура лише робить видимим контейнер [panelform]. Компоненти відображаються з їхнім поточним або попереднім значенням (VIEWSTATE).
Коли користувач натискає кнопку [Calculer] у вікні [formulaire], виконується перехід від POST до [main.aspx]. Виконується процедура [Page_Load], а потім — процедура [btnCalculer_Click]:
Private Sub btnCalculer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCalculer.Click
' перевіряється правильність введених даних; якщо є помилки, про це повідомляється
If Not Page.IsValid Then
afficheFormulaire()
Exit Sub
End If
' помилок немає — розраховується податок
Dim impot As Long = CType(Application("objImpot"), impot).calculer( _
rdOui.Checked, CType(txtEnfants.Text, Integer), CType(txtSalaire.Text, Long))
' результат додається до існуючих моделювань
Dim simulations As DataTable = CType(Session.Item("simulations"), DataTable)
Dim simulation As DataRow = simulations.NewRow
simulation("marié") = CType(IIf(rdOui.Checked, "oui", "non"), String)
simulation("enfants") = txtEnfants.Text.Trim
simulation("salaire") = CType(txtSalaire.Text.Trim, Long)
simulation("impôt") = impot
simulations.Rows.Add(simulation)
' моделювання додається до сесії
Session.Item("simulations") = simulations
' відображається сторінка моделювань
afficheSimulations(simulations, "Retour au formulaire")
End Sub
Процедура починається з перевірки валідності сторінки. Нагадаємо, що під час виконання процедури [btnCalculer_Click] перевірки валідності вже виконано, і атрибут [IsValid] сторінки встановлено. Якщо сторінка не є валідною, то знову відображається представлення [formulaire], і процедура завершується. Компоненти перевірки правильності у вигляді [formulaire], атрибут яких [IsValid] має значення від [false], відображатимуть свій атрибут [ErrorMessage]. Якщо сторінка є валідною, то сума податку обчислюється за допомогою об’єкта типу [impot], який було збережено в додатку під час його запуску. Ця нова симуляція додається до списку вже виконаних симуляцій і зберігається в сесії.
Нарешті, подання [simulations] відображається за допомогою наступної процедури [afficheSimulations]:
Private Sub afficheSimulations(ByRef simulations As DataTable, ByRef lien As String)
' пов'язуємо таблицю даних із джерелом симуляцій
With dgSimulations
.DataSource = simulations
.DataBind()
End With
' посилання
lnkForm2.Text = lien
' перегляди
panelsimulations.Visible = True
panelerreurs.Visible = False
panelform.Visible = False
End Sub
Процедура має два параметри:
- список симуляцій у [simulations] типу [DataTable]
- текст посилання у [lien]
Джерело даних [simulations] пов’язане з компонентом, що відповідає за його відображення. Текст посилання розміщується у властивості [Text] об’єкта [LinkButton] у поданні.
Коли користувач натискає кнопку [Effacer] у вікні [formulaire], виконується процедура [btnEffacer_click] (завжди після [Page_Load]):
Private Sub btnEffacer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEffacer.Click
' відображає порожню форму
razForm()
afficheFormulaire()
End Sub
Private Sub razForm()
' очищення форми
rdOui.Checked = False
rdNon.Checked = True
txtEnfants.Text = ""
txtSalaire.Text = ""
End Sub
Наведений вище код є достатньо простим, щоб не потребувати коментарів. Залишається лише обробити кліки на посиланнях у видах [erreurs] та [simulations]:
Private Sub lnkForm1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm1.Click
' відображає форму
afficheFormulaire()
End Sub
Private Sub lnkForm2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm2.Click
' відображає форму
afficheFormulaire()
End Sub
Обидві процедури просто викликають відображення виду [formulaire]. Ми знаємо, що поля цього виду отримають значення, яке є або значенням, що було для них відправлено, або їхнім попереднім значенням. Оскільки в даному випадку POST клієнта не надсилає жодних значень для полів форми, вони відновлять свої попередні значення. Отже, форма відображається із значеннями, введеними користувачем.
8.9.4. Тестування
Усі файли, необхідні для роботи додатка, розміщуються в папці <application-path>: ![]() | Папка [bin] містить DLL-файл із класами [impot], [impotsData], [impotsOLEDB], необхідними для роботи додатка: |
Читач може, за бажанням, ще раз ознайомитися з розділом 5, де пояснюється, як створити вищезазначений файл [impot.dll]. Після цього запускається сервер Cassini з параметрами (<application-path>,/impots6). За допомогою браузера запитується URL-адреса [http://impots6/main.aspx]:

Якщо перейменувати файл ACCESS [impots.mdb] на [impots1.mdb], з’явиться така сторінка:

8.9.5. Висновок
Ми маємо додаток MVC, який використовує виключно серверні компоненти. Використання цих компонентів дозволило повністю відокремити презентаційну частину додатка від його контрольної частини. Раніше це було неможливо, оскільки презентаційний код містив змінні, обчислені контрольним кодом, значення яких містили код HTML. Тепер це вже не так.






















