Skip to content

8. Серверні компоненти ASP — 2

8.1. Introduction

Ми продовжуємо роботу над користувацьким інтерфейсом, розглядаючи:

  • компоненти перевірки даних
  • спосіб прив’язування даних до серверних компонентів
  • серверні компоненти HTML

8.2. Компоненти перевірки даних

8.2.1. Вступ

У додатках з формами надзвичайно важливо перевіряти правильність даних, що вводяться в них. У прикладі з розрахунком податку ми мали таку форму:

Image

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

Image

ASP.NET пропонує компоненти, так звані компоненти перевірки, які дозволяють перевіряти такі випадки:

Компонент
Роль
RequiredFieldValidator
перевіряє, чи поле не є порожнім
CompareValidator
порівнює два значення між собою
RangeValidator
перевіряє, чи значення знаходиться в межах двох меж
RegularExpressionValidator
перевіряє, чи поле відповідає регулярному виразу
CustomValidator
дозволяє розробнику задавати власні правила перевірки — цей компонент може замінити всі інші
ValidationSummary
дозволяє зібрати повідомлення про помилки, видані попередніми елементами контролю, в одному місці на сторінці

Тепер ми розглянемо кожен із цих компонентів.

8.2.2. RequiredFieldValidator

Ми створюємо таку сторінку [requiredfieldvalidator1.aspx]:

назва
тип
властивості
роль
1
txtNom
TextBox
EnableViewState=true
поле введення
2
RequiredFieldValidator1
RequiredFieldValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=Поле [ім'я] є обов'язковим
компонент перевірки
3
btnEnvoyer
Button
EnableViewState=false
CausesValidation=true
кнопка [submit]

Поле [RequiredFieldValidator1] використовується для відображення повідомлення про помилку, якщо поле [txtNom] порожнє або містить послідовність пробілів. Його властивості такі:

ControlToValidate
поле, значення якого має перевірятися компонентом. Що означає значення компонента? Це значення атрибута [value] відповідного тегу HTML. Для [TextBox] це буде вміст поля введення, для [DropDownList] — значення вибраного елемента.
EnableClientScript
логічне значення — при значенні «true» вказує, що вміст попереднього поля має перевірятися також на стороні клієнта. У цьому випадку браузер відправить форму лише за умови, що вона не містить помилок. Проте перевірки валідації виконуються також на сервері на випадок, якщо клієнт не є, наприклад, браузером.
ErrorMessage
повідомлення про помилку, яке компонент повинен відобразити у разі виявлення помилки

Перевірка даних сторінки за допомогою елементів валідації виконується лише в тому випадку, якщо кнопка або посилання, що викликало [POST] на сторінці, має властивість [CausesValidation=true]. [true] — це значення за замовчуванням для цієї властивості.

Запустимо цю програму. Спочатку скористаємося браузером [Mozilla]:

Image

Код 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" />
                            &nbsp;
                        </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], не вказуючи імені. Відповідь сервера така:

Image

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

Image

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

Image

Код 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]

Ми отримаємо таку нову сторінку:

Image

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

Image

Що сталося? Браузер виконав код JavaScript для перевірки. Цей код визнав, що сторінка є валідною. Тому браузер відправив форму на веб-сервер, який був вимкнений. Браузер це виявив і відобразив наведену вище сторінку. Якщо перезапустити сервер, все повернеться до норми.

Що можна винести з цього прикладу?

  • важливість поняття «компонент перевірки», який дозволяє повернути клієнту будь-яку неправильно заповнену форму
  • важливість параметра [VIEWSTATE], який дозволяє повернути цю форму саме в тому вигляді, в якому вона була заповнена
  • здатність сервера адаптуватися до свого клієнта. Клієнт ідентифікується за заголовком HTTP [User-Agent:], який він надсилає серверу. Отже, це не сервер «вгадує», з ким він має справу. Ця здатність до адаптації є дуже важливою для розробника, якому не доводиться турбуватися про тип клієнта свого додатка.

8.2.3. CompareValidator

Ми створюємо таку сторінку [comparevalidator1.aspx]:

Image

назва
тип
властивості
роль
1
cmbChoix1
DropDownList
EnableViewState=true
випадаючий список
2
cmbChoix2
DropDownList
EnableViewState=true
випадаючий список
3
CompareValidator1
CompareValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=Неправильний вибір 1
Значення для порівняння=?
Operator=NotEqual
Тип=рядок
компонент перевірки
4
CompareValidator2
CompareValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=Неправильний вибір 2
ControlToCompare=?
Operator=Не дорівнює
Тип=рядок
компонент перевірки
5
btnEnvoyer
Button
EnableViewState=false
CausesValidation=true
кнопка [submit]

Компонент [CompareValidator] призначений для порівняння двох значень між собою. Можна використовувати оператори [Equal, NotEqual, LessThan, LessThanEqual, GreaterThan, GreaterThanEqual]. Перше значення задається властивістю [ControlToValidate], друге — властивістю [ValueToCompare], якщо попереднє значення потрібно порівняти з константою, або властивістю [ControlToCompare], якщо його потрібно порівняти зі значенням іншого компонента. Важливі властивості компонента [CompareValidator] такі:

ControlToValidate
поле, вміст якого має перевірятися компонентом
EnableClientScript
логічне значення — значення «true» вказує, що вміст попереднього поля також має перевірятися на стороні клієнта
ErrorMessage
повідомлення про помилку, яке компонент повинен відобразити у разі виявлення помилки
ValeurToCompare
значення, з яким має порівнюватися значення поля [ControlToValidate]
ControlToCompare
компонент, значення якого має бути порівняно зі значенням поля [ControlToValidate]
Operator
оператор порівняння між двома значеннями
Type
тип значень, що порівнюються

Код представлення цієї сторінки такий:

<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

Ми запускаємо цю програму. Ось приклад сторінки, отриманої під час обміну даними між клієнтом і сервером:

Image

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

8.2.4. CustomValidator, RangeValidator

Ми створюємо таку сторінку [customvalidator1.aspx]:

Image

назва
тип
властивості
роль
1
cmbDiplomes
DropDownList
EnableViewState=true
випадаючий список
2
CompareValidator2
CompareValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=Недійсний диплом
Operator=NotEqual
ValueToCompare=?
Type=String
компонент перевірки елемента управління [1]
3
txtAutreDiplome
TextBox
EnableViewState=false
поле введення
4
CustomValidator1
CustomValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=Неправильна точність диплома
ClientValidationFunction=chkAutreDiplome
компонент перевірки поля [3]
5
txtAnDiplome
TextBox
EnableViewState=false
поле введення
6
RangeValidator1
RangeValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=Рік отримання диплома повинен бути в діапазоні [1990,2004]
MinValue=1990
MaxValue=2004
Тип=Ціле число
ControlToValidate=txtAnDiplome
компонент перевірки поля [5]
7
RequiredFieldValidator2
RequiredFieldValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=Необхідно вказати рік отримання диплома
ControlToValidate=txtAnDiplome
компонент перевірки поля [5]
8
btnEnvoyer
Button
EnableViewState=false
CausesValidation=true
кнопка [submit]

Поле [RangeValidator] використовується для перевірки того, чи значення елемента управління знаходиться в межах двох меж [MinValue] та [MaxValue]. Його властивості такі:

ControlToValidate
поле, значення якого має перевірятися компонентом
EnableClientScript
логічне значення — якщо «true», вказує, що вміст попереднього поля також має перевірятися на стороні клієнта
ErrorMessage
повідомлення про помилку, яке компонент повинен відобразити у разі виявлення помилки
MinValue
мінімальне значення поля, яке потрібно перевірити
MaxValue
максимальне значення поля, що перевіряється
Type
тип значення поля, що підлягає перевірці

Поле [CustomValidator] дозволяє виконувати перевірки, які не можуть виконувати компоненти перевірки, запропоновані ASP.NET. Ця перевірка здійснюється за допомогою функції, написаної розробником. Вона виконується на стороні сервера. Оскільки перевірка може здійснюватися також на стороні клієнта, розробнику може знадобитися розробити функцію JavaScript, яку він включить у документ HTML. Її властивості такі:

ControlToValidate
поле, значення якого має перевірятися компонентом
EnableClientScript
логічне значення — якщо «true», вказує, що вміст попереднього поля також має перевірятися на стороні клієнта
ErrorMessage
повідомлення про помилку, яке компонент повинен відобразити у разі виявлення помилки
ClientValidationFunction
функція, яку потрібно виконати на стороні клієнта

Код для відображення сторінки такий:

<%@ 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&#238;trise">Ma&#238;перевірка</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]:

Image

Якщо ми використовуємо браузер [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]:

Image

назва
тип
властивості
роль
1
txtMel
TextBox
EnableViewState=true
поле введення
2
RequiredFieldValidator3
RequiredFieldValidator
ControlToValidate=txtMel
Display=Dynamic
компонент перевірки [1]
3
RegularExpressionValidator1
RegularExpressionValidator
ControlToValidate=txtMel
Display=Dynamic
компонент перевірки елемента управління [1]
4
btnEnvoyer
Button
EnableViewState=false
CausesValidation=true
кнопка [submit]

Тут ми хочемо перевірити формат електронної адреси. Ми робимо це за допомогою компонента [RegularExpressionValidator], який дозволяє перевірити правильність поля за допомогою регулярного виразу. Нагадаємо, що регулярний вираз — це шаблон символьного рядка. Отже, ми перевіряємо вміст поля на відповідність шаблону. Оскільки вміст поля не перевіряється, якщо воно порожнє, нам також потрібен компонент [RequiredFieldValidator], щоб перевірити, чи електронна адреса не порожня. Клас [RegularExpressionValidator] має властивості, аналогічні до класів компонентів, які ми вже розглядали:

ControlToValidate
поле, значення якого має перевірятися компонентом
EnableClientScript
булеве значення — якщо «true», вказує, що вміст попереднього поля також має перевірятися на стороні клієнта
ErrorMessage
повідомлення про помилку, яке компонент повинен відобразити у разі виявлення помилки
RegularExpression
регулярний вираз, з яким буде порівнюватися вміст поля [ControlToValidate]
Display
режим відображення: 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], де ми зібрали на одній сторінці всі попередні приклади:

Image

Усі елементи перевірки мають такі властивості:

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

Цей набір елементів управління розміщений у компоненті [Panel], який називається [vueFormulaire]. В іншому компоненті [Panel], який називається [vueErreurs], ми розміщуємо елемент управління [ValidationSummary]:

Image

назва
тип
властивості
роль
1
ValidationSummary1
ValidationSummary
HeaderText=Сталися такі помилки, EnableClientScript=false, ShowSummary=true
відображає помилки всіх елементів перевірки на сторінці
2
lnkErreursToFormulaire
LinkButton
CausesValidation=false
посилання для повернення до форми

Для посилання [lnkErreursToFormulaire] немає необхідності активувати перевірку, оскільки це посилання не надсилає жодних значень для перевірки.

Коли всі дані є правильними, користувачеві відображається такий вигляд [infos]:

Image

1

ім’я
тип
властивості
роль
1
lblInfo
Мітка
 
інформаційне повідомлення для користувача

Код форматування цієї сторінки такий:

<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], не вводячи жодних значень:

Image

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

Image

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

Image

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

Image

Код управління сторінкою такий:

<%@ 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], має такий вигляд:

DropDownList, ListBox
<option value="V">T</option>
CheckButtonList
<input type="checkbox" value="V">T
RadioButtonList
<input type="radio" value="V">T

Прив'язка компонента [ListControl] до джерела даних здійснюється за допомогою таких властивостей:

DataSource
джерело даних [Array], [ArrayList], [DataTable], [DataSet], [HashTable], ...
DataMember
у випадку, коли джерелом даних є [DataSet], позначає назву таблиці, яку слід використовувати як джерело даних. У цьому випадку справжнім джерелом даних є таблиця.
DataTextField
у випадку, коли джерелом даних є таблиця ([DataTable], [DataSet]), позначає ім’я стовпця таблиці, який надаватиме свої значення полю [Text] елементів [ListControl]
DataValueField
у випадку, коли джерелом даних є таблиця ([DataTable], [DataSet]), позначає ім’я стовпця таблиці, який надаватиме свої значення полю [Value] елементів [ListControl]

Прив’язування компонента [ListControl] до джерела даних не ініціалізує компонент значеннями з джерела даних. Це робить операція [ListControl].DataBind.

Залежно від типу джерела даних, його прив’язування до компонента [ListControl] відбуватиметься по-різному:

Array A
[ListControl].DataSource=A
поля [Text] та [Value] елементів [ListControl] матимуть значення елементів A
ArrayList AL
[ListControl].DataSource=AL
поля [Text] та [Value] елементів [ListControl] матимуть значення елементів AL
DataTable DT
[ListControl].DataSource=DT, [ListControl].DataTextField = «col1», [ListControl]. DatavalueField = «col2»
де col1 і col2 — два стовпці таблиці DT. Поля [Text] та [Value] елементів таблиці [ListControl] матимуть значення, що відповідають значенням стовпців col1 та col2 таблиці DT
DataSet DS
[ListControl].DataSource=DS, [ListControl].DataSource="table", де "table" — це назва однієї з таблиць DS.
[ListControl].DataTextField = «col1», [ListControl]. DatavalueField = «col2», де col1 і col2 — два стовпці таблиці «table». Поля [Text] та [Value] елементів [ListControl] матимуть значення, що відповідають стовпцям col1 та col2 таблиці «table»
HashTable HT
[ListControl].DataSource=HT, [ListControl].DataTextField="key", [ListControl]. DatavalueField ="value", де [key] та [value] є відповідно ключами та значеннями HT.

Застосуємо цю інформацію на прикладі [databind1.aspx]:

Тут ми маємо п’ять зв’язків даних, кожен із яких містить чотири елементи управління типу [DropDownList], [ListBox], [CheckBoxList] та [RadioButtonList]. П’ять розглянутих зв’язків відрізняються за джерелами даних:

канал
джерело даних
1
Масив
2
ArrayList
3
DataTable
4
DataSet
5
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], причому кожному з них присвоюється:

            .DataSource= myDataTable
            .DataValueField="id"
            .DataTextField="texte"

Таблиця [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] пов'язаний із джерелом даних таким чином:

            .DataSource= myDataSet
            .DataMember="table1"
            .DataValueField="id"
            .DataTextField="texte"

Група даних [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

Для кожного з компонентів зв'язок здійснюється за допомогою таких інструкцій:

            .DataSource=myHashTable
            .DataValueField="key"
            .DataTextField="value"

Джерелом даних є словник [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]:

DataSource
джерело даних [Array], [ArrayList], [DataTable], [DataSet], ...
DataMember
у випадку, коли джерелом даних є [DataSet], вказує назву таблиці, яку слід використовувати як джерело даних. У такому разі справжнім джерелом даних є таблиця. Якщо це поле не заповнено, відображаються всі таблиці з [DataSet].

Тепер ми покажемо сторінку [datagrid1.aspx], на якій показано зв’язок [DataGrid] із чотирма різними джерелами даних:

Image

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

Image

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

Image

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

Image

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

Image

Ми обираємо [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>

Код досить схожий на той, що наведено в попередньому прикладі, тому ми не будемо його окремо коментувати. Однак звернімо увагу на те, як здійснюється зв’язування даних. Для кожного з чотирьох елементів управління достатньо такої послідовності:

          with [DataGrid]
            .DataSource=[source de données]
            .DataBind
          end with

де [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
DataGrid1
DataGrid
EnableViewState=true
відображає джерело даних S
2
DataGrid2
DataGrid
EnableViewState=true
відображає те саме джерело S, що й [DataGrid1]
3
Button1
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] під час кожного нового запиту.

Під час першого запиту ми отримуємо таке зображення:

Image

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

Image

Ми бачимо, що компонент [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
DataGrid1
DataGrid
EnableViewState=true
відображає джерело даних S
2
DataGrid2
DataGrid
EnableViewState=false
відображає те саме джерело S, що й [DataGrid1]
3
Button1
Button
EnableViewState=false
кнопка [submit]
4
lblInfo1
lblInfo2
lblInfo3
Етикетка
EnableViewState=false
інформаційні тексти

Компонент [DataGrid1] пов'язується з даними лише під час першого запиту. Він зберігатиме своє значення протягом усіх запитів завдяки механізму [viewstate]. Компонент [DataGrid2] пов'язується під час кожного запиту з джерелом даних, до якого під час кожного нового запиту додається новий елемент. Тому необхідно пов'язати (DataBind) компонент [DataGrid2] з кожним запитом. Тому ми встановили його атрибут [EnableViewState] на [faux], як було рекомендовано раніше. Таким чином, під час другого запиту (за допомогою кнопки [Envoyer]) ми отримуємо таку відповідь:

Image

Компонент [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&nbsp;:
                <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") = 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

щоб виконати це збереження, оскільки 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]. Об’єкт, на який посилаються ці елементи, може бути змінений як через одне, так і через інше посилання.

З цього випливає, що немає сенсу писати:

        Session("numRequête3") = numRequête3

для збереження нового значення [numRequête3] у сесії. Натомість слід написати:

        Session("numRequête1") = numRequête1
        Session("numRequête2") = numRequête2

щоб зберегти нові значення структур [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:

Image

Код класу [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
txtSelect
TextBox
EnableViewState=true
поле введення запиту select
2
RequiredFieldValidator1
RequiredFieldValidator
EnableViewState=false
перевіряє наявність 1
3
txtPages
TextBox
EnableViewState=true
поле введення — вказує кількість рядків даних, що відображаються на одній сторінці результатів
4
RequiredFieldValidator2
RequiredFieldValidator
EnableViewState=false
перевіряє наявність 3
5
RangeValidator1
RangeValidator
EnableViewState=false
перевіряє, чи (3) знаходиться в діапазоні [1,30]
6
btnExécuter
Button
EnableViewState=false
кнопка [submit]

Цей вигляд ми назвемо [formulaire]. Він дозволяє користувачеві виконати запит SQL select у базі даних [produits.mdb]. Виконання запиту створює новий вигляд:

назва
тип
властивості
роль
1
lblSelect
Мітка
EnableViewState=false
інформаційне поле
2
rdCroissant
rdDécroissant
RadioButton
EnableViewState=false
GroupName=rdTri
дозволяє вибрати напрямок сортування
3
DataGrid1
DataGrid
EnableViewState=true
AllowPaging=true
AllowSorting=true
таблиця для відображення результатів вибірки
4
lnkRésultats
LinkButton
EnableViewState=false
кнопка-посилання [submit]

Цей вигляд ми назвемо [résultats]. Саме він містить [DataGrid], який відображатиме результат команди SQL select. Користувач може помилитися у своєму запиті. Про деякі помилки йому буде повідомлено у вікні [erreurs] завдяки елементам перевірки.

Image

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

Image

Відображається вікно [erreurs]:

назва
тип
властивості
роль
1
erreursHTML
змінна
 
код HTML, необхідний для відображення помилок
3
lnkForm2
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>&nbsp;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&#233;c&#233;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&#233;c&#233;dent" ...></PagerStyle>
                    </asp:DataGrid></P>
AllowPaging="true"
дозволяє пагінацію
PageSize="4"
чотири рядки даних на сторінку
NextPageText="Suivant"
Текст посилання для переходу на наступну сторінку джерела даних
PrevPageText="Précédent"
Текст посилання для переходу на попередню сторінку джерела даних

Цю інформацію можна ввести безпосередньо в атрибути тегу <asp:datagrid>. Також можна скористатися [WebMatrix]. У вікні властивостей [DataGrid] перейдіть за посиланням [Générateur de propriétés]:

Image

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

Image

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

Image

Вище наведено значення атрибутів пагінації [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]

<%@ Application src="global.asax.vb" inherits="Global" %>

[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], перевірки, пов’язані з різними елементами валідації сторінки, вже виконано. Для кожного елемента валідації встановлено два атрибути:

IsValid
приймає значення «true», якщо перевірені дані є валідними, і «false» — у протилежному випадку
ErrorMessage
повідомлення про помилку, якщо перевірені дані виявилися недійсними

Для самої сторінки було встановлено атрибут [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
source
об’єкт, що ініціював подію — у даному випадку одне з посилань [Suivant] або [Précédent]
e
інформація про подію. Атрибут 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
source
об’єкт, що ініціював подію — у даному випадку одне з посилань [Suivant] або [Précédent]
e
інформація про подію. Атрибут [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
RadioButton1
RadioButton2
RadioButton
EnableViewState=false
дозволяє вибрати один із двох стилів [DataList]
2
btnChanger
Button
EnableViewState=false
кнопка [submit]
3
DataList1
DataList
EnableViewState=true
поле відображення списку даних

Якщо користувач вибере стиль № 2, він отримає такий вигляд: [résultats2]:

назва
тип
властивості
роль
1
DataList2
DataList
EnableViewState=true
поле відображення списку даних

Вигляд [erreurs] повідомляє про проблему з доступом до джерела даних:

назва
тип
властивості
роль
1
erreursHTML
змінна
 
код 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] можна визначити до семи шаблонів відображення:

HeaderTemplate
шаблон заголовка [DataList]
ItemTemplate
шаблон рядків, що відображають елементи пов'язаного списку даних. Обов'язковим є лише цей шаблон.
AlternatingItemTemplate
щоб візуально розрізнити послідовні елементи, що відображаються, можна використовувати два шаблони: ItemTemplate для елемента n, AlternatingItemTemplate для елемента n+1
SelectedItemTemplate
шаблон вибраного елемента в [DataList]
SeparatorTemplate
шаблон розділювача між двома елементами у [DataList]
EditItemTemplate
[DataList] дозволяє змінювати значення, які він відображає. [EditItemTemplate] — це шаблон елемента з [DataList], для якого активний режим «редагування»
FooterTemplate
шаблон нижнього колонтитулу для [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>

Шаблон не визначено. Це ми маємо зробити. Ми визначаємо такі шаблони:

HeaderTemplate

                        <HeaderTemplate>
                            Contenu de la table [liste] de la base [produits]
                        </HeaderTemplate>
ItemTemplate

                        <ItemTemplate>
                            nom :
                            <%# Container.DataItem("назва") %>
                            <br>
                            prix :
                            <%# DataBinder.Eval(Container.DataItem,"ціна","{0:C}") %>
                            <br>
                        </ItemTemplate>

Нагадаємо, що модель [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], який виглядатиме так:

Image

У наведеному вище прикладі дані розміщено по чотири записи в рядку. Це досягається за допомогою таких атрибутів [DataList]:

RepeatDirection
Горизонтально
RepeatColumns
бажана кількість стовпців

У підсумку код [DataList1] є тим, що було представлено у коді відображення трохи вище. Ми залишаємо читачеві можливість самостійно вивчити код відображення [DataList2]. Як і у випадку з компонентом [DataGrid], більшість властивостей [DataList] можна налаштувати за допомогою майстра [WebMatrix]. Для цього використовується посилання [Générateur de propriétés] у вікні властивостей [DataList]:

8.7.5. Елементи управління

Контролер [global.asax, global.asax.vb] має такий вигляд:

[global.asax]

<%@ Application src="global.asax.vb" inherits="Global" %>

[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 для кожного елемента списку даних. Припустимо, що потрібно відобразити список помилок у такому вигляді:

Image

Ми вже стикалися з цією проблемою і вирішили її, вставивши в код відображення змінну у вигляді <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] представляє рядок даних, якщо джерело даних має кілька стовпців. Воно представляє одне значення, якщо джерело має лише один стовпець. Саме так і буде в даному випадку. Для прикладу, ми створюємо такий додаток:

Image

Код для відображення сторінки такий:

<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] — це форма введення даних, що дозволяє розрахувати податок для користувача:

Image

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

Image

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

Image

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

Image

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

Image

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

Image

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

Image

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

Image

8.9.2.1. Код відображення

Нагадаємо, що сторінка [main.aspx] об’єднує всі перегляди. Це єдина форма з трьома контейнерами:

  • [panelform] для подання [formulaire]
  • [panelerreurs] для подання [erreurs]
  • [panelsimulations] для подання [simulations]

Тепер детально розглянемо компоненти цих трьох контейнерів. Контейнер [panelform] має таке візуальне представлення:

назва
тип
властивості
роль
0
panelform
Панель
 
вигляд форми
1
rdOui
rdNon
RadioButton
GroupName=rdmarie
перемикачі
2
cvMarie
CustomValidator
ErrorMessage=Ви не вказали свій сімейний стан
EnableClientScript=false
перевіряє, чи клієнтська програма надіслала очікуваний сімейний стан
3
txtEnfants
TextBox
 
кількість дітей
4
rfvEnfants
RequiredFieldValidator
ErrorMessage=Вкажіть кількість дітей
ControlToValidate=txtEnfants
перевіряє, чи поле [txtEnfants] не порожнє
5
rvEnfants
RangeValidator
ErrorMessage=Введіть число від 1 до 30
ControlToValidate=txtEnfants
перевіряє, чи поле [txtEnfants] знаходиться в діапазоні [1,30]
6
txtSalaire
TextBox
EnableViewState=true
річний оклад
7
rfvSalaire
RequiredFieldValidator
ControlToValidate=txtSalaire
ErrorMessage=Вкажіть суму своєї заробітної плати
перевіряє, чи поле [txtSalaire] не порожнє
8
revSalaire
RegularExpressionValidator
ControlToValidate=txtSalaire
ErrorMessage=Неправильна заробітна плата
RegularExpression=\s*\d+\s*
перевіряє, чи поле [txtSalaire] є послідовністю цифр
9
btnCalculer
Button
CausesValidation=true
кнопка [submit] у формі — запускає розрахунок податку
10
btnEffacer
Кнопка
CausesValidation=false
кнопка [submit] у формі — очищає форму

Може викликати подив елемент контролю [cvMarié], який відповідає за перевірку того, чи користувач дійсно встановив прапорець на одному з двох перемикачів. Адже інакше бути не може, якщо він використовує саме ту форму, яку надіслав сервер. Оскільки ніщо не може цього гарантувати, доводиться перевіряти всі надіслані параметри. Також слід звернути увагу на атрибут [CausesValidation=false] кнопки [btnEffacer]. Адже коли користувач натискає цю кнопку, відправлені дані перевіряти не потрібно, оскільки вони будуть проігноровані.

Усі компоненти контейнера мають властивість [EnableViewState=false], крім [panelForm, txtEnfants, txtSalaire, rdOui, rdNon]. Контейнер [panelerreurs] має таке візуальне представлення:

назва
тип
властивості
роль
0
panelerreurs
Панель
EnableViewState=false
перегляд помилок
1
rptErreurs
Повторювач
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] має таке візуальне представлення:

назва
тип
властивості
роль
0
panelsimulations
Панель
EnableViewState=false
перегляд симуляцій
2
lnkForm2
LinkButton
EnableViewState=false
посилання на форму
1
dgSimulations
DataGrid
EnableViewState=false
відповідає за відображення симуляцій, розміщених в об’єкті [DataTable]

Компонент [dgSimulations] налаштований наступним чином у [Webmatrix]. У вікні властивостей [dgSimulations] ми вибираємо посилання [Mise en forme automatique] і вибираємо схему [Couleur 3]:

Потім, не виходячи з вікна властивостей [dlgSimulations], вибираємо посилання [Générateur de propriétés]. Вимагаємо відображення заголовків стовпців:

Image

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

Image

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

Image

Texte de l'en-tête
заголовок стовпця, у даному випадку «Одружений»
Champ de données
назва стовпця джерела даних, який буде відображатися цим стовпцем [DataGrid]. У даному випадку це стовпець «одружений» з [DataTable].

Другий стовпець визначається наступним чином:

Texte de l'en-tête
Діти
Champ de données
діти

Третій стовпець визначається наступним чином:

Texte de l'en-tête
Річний оклад
Champ de données
зарплата
Expression de mise en forme
{0:C} — форматування валюти для відображення знака євро

Четвертий стовпець визначається наступним чином:

Texte de l'en-tête
Сума податку
Champ de données
податок
Expression de mise en forme
{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&#233;" HeaderText="Mari&#233;"></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&#244;t" HeaderText="Montant de l'imp&#244;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] має такий вигляд:

<%@ Application src="Global.asax.vb" Inherits="Global" %>

Файл [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]:

Image

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

Image

8.9.5. Висновок

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