Skip to content

8. ASP-Serverkomponenten – 2

8.1. Einführung

Wir setzen unsere Arbeit an der Benutzeroberfläche fort und befassen uns mit:

  • Komponenten zur Datenvalidierung
  • wie man Daten an Serverkomponenten bindet
  • HTML-Serverkomponenten

8.2. alle Datenvalidierungskomponenten

8.2.1. Einführung

In formularbasierten Anwendungen ist es unerlässlich, die Gültigkeit der eingegebenen Daten zu überprüfen. Im Beispiel zur Steuerberechnung hatten wir das folgende Formular:

Image

Nach dem Empfang der Werte aus diesem Formular müssen wir die Gültigkeit der eingegebenen Daten überprüfen: Die Anzahl der Kinder und das Gehalt müssen positive ganze Zahlen oder Null sein. Ist dies nicht der Fall, wird das Formular zusammen mit Fehlermeldungen unverändert an den Client zurückgesendet. Wir haben dieses Szenario mit zwei Ansichten umgesetzt, eine für das obige Formular und die andere für die Fehler:

Image

ASP.NET bietet sogenannte Validierungskomponenten, mit denen Sie Folgendes überprüfen können:

Komponente
Rolle
RequiredFieldValidator
prüft, ob ein Feld nicht leer ist
CompareValidator
vergleicht zwei Werte miteinander
RangeValidator
prüft, ob ein Wert zwischen zwei Grenzen liegt
RegularExpressionValidator
prüft, ob ein Feld einem regulären Ausdruck entspricht
CustomValidator
ermöglicht es dem Entwickler, eigene Validierungsregeln zu definieren – diese Komponente könnte alle anderen ersetzen
ValidationSummary
ermöglicht es Ihnen, die von den vorherigen Steuerelementen generierten Fehlermeldungen an einer einzigen Stelle auf der Seite zusammenzufassen

Wir werden nun jede dieser Komponenten vorstellen.

8.2.2. RequiredFieldValidator

Wir erstellen die folgende Seite [requiredfieldvalidator1.aspx]:

Nr.
name
Typ
Eigenschaften
Rolle
1
txtName
Textfeld
EnableViewState=true
Eingabefeld
2
ErforderlichesFeldValidator1
RequiredFieldValidator
EnableViewState=false
Client-Skript aktivieren=true
ErrorMessage=Das Feld [name] ist erforderlich
Validierungskomponente
3
btnSubmit
Schaltfläche
EnableViewState=false
ValidationReasons=true
[submit]-Schaltfläche

Das Feld [RequiredFieldValidator1] wird verwendet, um eine Fehlermeldung anzuzeigen, wenn das Feld [txtName] leer ist oder eine Zeichenfolge aus Leerzeichen enthält. Seine Eigenschaften sind wie folgt:

ControlToValidate
Das Feld, dessen Wert von der Komponente validiert werden muss. Was bedeutet der Wert einer Komponente? Es handelt sich um den Wert des Attributs [value] des entsprechenden HTML-Tags. Bei einem [TextBox] ist dies der Inhalt des Eingabefelds; bei einem [DropDownList] ist es der Wert des ausgewählten Elements.
EnableClientScript
Boolean – Wenn „true“, gibt dies an, dass der Inhalt des vorherigen Feldes auch auf der Client-Seite validiert werden muss. In diesem Fall wird das Formular vom Browser nur übermittelt, wenn es keine Fehler enthält. Validierungsprüfungen werden jedoch auch auf dem Server durchgeführt, falls der Client beispielsweise kein Browser ist.
ErrorMessage
Die Fehlermeldung, die die Komponente anzeigen muss, wenn ein Fehler erkannt wird

Die Datenvalidierung auf der Seite wird nur durchgeführt, wenn die Schaltfläche oder der Link, der den [POST] der Seite ausgelöst hat, die Eigenschaft [CausesValidation=true] besitzt. [true] ist der Standardwert für diese Eigenschaft.

Lassen Sie uns diese Anwendung ausführen. Zunächst verwenden wir einen [Mozilla]-Browser:

Image

Der von [Mozilla] empfangene HTML-Code lautet wie folgt:

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

Wir sehen, dass die Schaltfläche [btnEnvoyer] über ihr [onclick]-Attribut mit einer JavaScript-Funktion verknüpft ist. Wir sehen auch, dass die Seite keinen [JavaScript]-Code enthält. Der Test [typeof(Page_ClientValidate) == 'function'] schlägt fehl, und die JavaScript-Funktion wird nicht aufgerufen. Das Formular wird dann an den Server übermittelt, der die Validierungsprüfungen durchführt. Verwenden wir die Schaltfläche [Submit], ohne einen Namen einzugeben. Die Antwort des Servers lautet wie folgt:

Image

Was ist passiert? Das Formular wurde an den Server gesendet. Der Server hat den Code für alle Validierungskomponenten auf der Seite ausgeführt. Hier gibt es nur eine. Wenn mindestens eine Validierungskomponente einen Fehler feststellt, gibt der Server das Formular so zurück, wie es eingegeben wurde (tatsächlich enthält es bei Komponenten mit [EnableViewState=true] auch eine Fehlermeldung für jede Validierungskomponente, die einen Fehler festgestellt hat). Diese Meldung ist das [ErrorMessage]-Attribut der Validierungskomponente. Dies ist der Mechanismus, den wir oben in Aktion sehen. Wenn wir etwas in das Feld [name] eingeben, erscheint die Fehlermeldung nicht mehr, wenn das Formular gesendet wird:

Image

Verwenden wir nun den Internet Explorer und rufen die URL [http://localhost/requiredfieldvalidator1.aspx] auf. Wir erhalten die folgende Seite:

Image

Der vom IE empfangene HTML-Code lautet wie folgt:

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

Wir sehen, dass dieser Code viel länger ist als der von [Mozilla] empfangene. Wir werden nicht auf die Details eingehen. Der Unterschied rührt daher, dass der Server JavaScript-Funktionen in das gesendete HTML-Dokument eingebunden hat. Warum gibt es zwei verschiedene HTML-Codes, obwohl die von beiden Browsern angeforderte URL identisch ist? Wir haben bereits erwähnt, dass die ASP.NET-Technologie bewirkt, dass der Server das an den Client gesendete HTML-Dokument an die spezifische Umgebung dieses Clients anpasst. Es gibt verschiedene Browser auf dem Markt, und nicht alle verfügen über die gleichen Funktionen. Microsoft- und Netscape-Browser verwenden beispielsweise nicht dasselbe Objektmodell für das empfangene Dokument. Folglich unterscheidet sich der JavaScript-Code, der zur Bearbeitung dieses Dokuments auf der Client-Seite verwendet wird, zwischen den beiden Browsern. Ebenso haben Browser-Anbieter sukzessive neue Versionen ihrer Browser veröffentlicht und deren Funktionen kontinuierlich verbessert. Daher kann ein HTML-Dokument, das IE5 auswerten kann, von IE3 möglicherweise nicht verstanden werden. Oben ist ein Beispiel für diese serverseitige Anpassung an den Client dargestellt. Aus einem hier nicht näher untersuchten Grund stellte der Webserver fest, dass dem Client [Mozilla] die Fähigkeit fehlte, den JavaScript-Code zur Validierung zu verarbeiten. Daher wurde dieser Code nicht in das an ihn gesendete HTML-Dokument aufgenommen, und die Validierung wurde auf der Serverseite durchgeführt. Für [IE6] wurde dieser JavaScript-Code, wie wir sehen können, in das gesendete HTML-Dokument aufgenommen. Um dies in Aktion zu sehen, führen wir folgendes Experiment durch:

  • Stoppen Sie den Webserver
  • klicken Sie auf die Schaltfläche [Absenden], ohne das Feld [Name] auszufüllen

Wir erhalten die folgende neue Seite:

Image

Es ist tatsächlich der Browser, der die Fehlermeldung angezeigt hat. Das liegt daran, dass der Server angehalten ist. Wir können dies überprüfen, indem wir etwas in das Feld [Name] eingeben und absenden. Diesmal lautet die Antwort wie folgt:

Image

Was ist passiert? Der Browser hat den JavaScript-Validierungscode ausgeführt. Dieser Code stellte fest, dass die Seite gültig war. Der Browser hat das Formular dann an den Webserver gesendet, der jedoch angehalten war. Der Browser hat dies erkannt und die oben gezeigte Seite angezeigt. Wenn wir den Server neu starten, kehrt alles zum Normalzustand zurück.

Was können wir aus diesem Beispiel lernen?

  • den Wert des Konzepts der Validierungskomponente, das es ermöglicht, jedes fehlerhafte Formular an den Client zurückzusenden
  • den Wert von [VIEWSTATE], wodurch das Formular genau so zurückgegeben werden kann, wie es eingegeben wurde
  • die Fähigkeit des Servers, sich an seinen Client anzupassen. Der Client wird durch den HTTP-Header [User-Agent:] identifiziert, den er an den Server sendet. Es ist also nicht der Server, der „errät“, mit wem er es zu tun hat. Diese Anpassungsfähigkeit ist für den Entwickler von großem Interesse, da er sich keine Gedanken über den Client-Typ für seine Anwendung machen muss.

8.2.3. CompareValidator

Wir erstellen die folgende Seite [comparevalidator1.aspx]:

Image

Nr.
name
Typ
Eigenschaften
Rolle
1
cmbChoix1
DropDownList
EnableViewState=true
Dropdown-Liste
2
cmbChoix2
DropDownList
EnableViewState=true
Dropdown-Liste
3
CompareValidator1
CompareValidator
EnableViewState=false
Client-Skript aktivieren=true
Fehlermeldung=Ungültige Auswahl 1
Zu vergleichender Wert=?
Operator=Nicht gleich
Typ=Zeichenkette
Validierungskomponente
4
CompareValidator2
CompareValidator
ViewState aktivieren=false
Client-Skript aktivieren=true
Fehlermeldung=Ungültige Auswahl 2
Zu vergleichendes Steuerelement=?
Operator=Nicht gleich
Typ=Zeichenkette
Validierungskomponente
5
btnSend
Schaltfläche
EnableViewState=false
ValidationReasons=true
[submit]-Schaltfläche

Die Komponente [CompareValidator] dient zum Vergleichen zweier Werte. Die verfügbaren Operatoren sind [Equal, NotEqual, LessThan, LessThanEqual, GreaterThan, GreaterThanEqual]. Der erste Wert wird durch die Eigenschaft [ControlToValidate] festgelegt, der zweite durch [ValueToCompare], wenn der erste Wert mit einer Konstante verglichen werden soll, oder durch [ControlToCompare], wenn er mit dem Wert einer anderen Komponente verglichen werden soll. Die wichtigen Eigenschaften der Komponente [CompareValidator] sind wie folgt:

ControlToValidate
Feld, dessen Inhalt von der Komponente validiert werden muss
EnableClientScript
Boolean – wenn true, gibt an, dass der Inhalt des vorangehenden Feldes auch auf der Client-Seite validiert werden muss
Fehlermeldung
die Fehlermeldung, die die Komponente anzeigen soll, wenn ein Fehler erkannt wird
ValueToCompare
der Wert, mit dem der Wert des Feldes [ControlToValidate] verglichen werden muss
ControlToCompare
die Komponente, mit deren Wert der Wert des Feldes [ControlToValidate] verglichen werden muss
Operator
Vergleichsoperator zwischen den beiden Werten
Typ
Typ der zu vergleichenden Werte

Der HTML-Code für diese Seite lautet wie folgt:

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

Die Validierungsbedingungen für die Seite lauten wie folgt:

  • Der in [cmbChoix1] ausgewählte Wert muss sich von der Zeichenfolge „?“ unterscheiden. Dies impliziert die folgenden Eigenschaften für die Komponente [CompareValidator1]: Operator=NotEqual, ValueToCompare=?, Type=string
  • Der in [cmbChoix2] ausgewählte Wert muss sich von dem in [cmbChoix1] ausgewählten Wert unterscheiden. Dies impliziert die folgenden Eigenschaften für die Komponente [CompareValidator2]: Operator=NotEqual, ControlToCompare=cmbChoix1, Type=string

Wir führen diese Anwendung aus. Hier ist ein Beispiel für eine Seite, die während des Client-Server-Austauschs empfangen wurde:

Image

Wird dieselbe Seite von Internet Explorer 6 aufgerufen, enthält sie JavaScript-Code, der zu einem leicht abweichenden Verhalten führt. Etwaige Fehler werden gemeldet, sobald der Benutzer einen Wert in einer der Dropdown-Listen ändert. Dies verbessert die Benutzererfahrung.

8.2.4. CustomValidator- , RangeValidator

Wir erstellen die folgende Seite [customvalidator1.aspx]:

Image

Nr.
name
Typ
Eigenschaften
Rolle
1
cmbDiplomas
DropDownList
EnableViewState=true
Dropdown-Liste
2
Vergleichsvalidator2
CompareValidator
EnableViewState=false
Client-Skript aktivieren=true
Fehlermeldung=Ungültiges Diplom
Operator=Nicht gleich
ZuVergleichenderWert=?
Typ=Zeichenkette
Validierungskomponente des Steuerelements [1]
3
txtOtherDegree
Textfeld
EnableViewState=false
Eingabefeld
4
CustomValidator1
CustomValidator
EnableViewState=false
Client-Skript aktivieren=true
ErrorMessage=Ungültige Angabe des Abschlusses
ClientValidationFunction=chkOtherDegree
Validierungskomponente für Feld [3]
5
txtAnDiploma
Textfeld
EnableViewState=false
Eingabefeld
6
Bereichsprüfung1
Bereichsprüfung
EnableViewState=false
Client-Skript aktivieren=true
ErrorMessage=Das Abschlussjahr muss im Bereich [1990,2004] liegen
MinValue=1990
MaxValue=2004
Typ=Ganzzahl
ControlToValidate=txtAnDiplome
Feldvalidierungskomponente [5]
7
RequiredFieldValidator2
RequiredFieldValidator
EnableViewState=false
Client-Skript aktivieren=true
ErrorMessage=Abschlussjahr erforderlich
Zu validierendes Steuerelement=txtAnDiplome
Feldvalidierungskomponente [5]
8
btnSubmit
Schaltfläche
EnableViewState=false
ValidationReasons=true
[submit]-Schaltfläche

Das Feld [RangeValidator] dient dazu, zu überprüfen, ob der Wert eines Steuerelements zwischen den beiden Grenzwerten [MinValue] und [MaxValue] liegt. Seine Eigenschaften sind wie folgt:

ControlToValidate
Feld, dessen Wert von der Komponente validiert werden muss
EnableClientScript
Boolean – Wenn „true“, gibt dies an, dass der Inhalt des vorherigen Feldes auch auf der Client-Seite validiert werden muss
ErrorMessage
Die Fehlermeldung, die die Komponente anzeigen soll, wenn ein Fehler erkannt wird
MinValue
Mindestwert des zu validierenden Feldes
MaxValue
Maximalwert des zu prüfenden Feldes
Typ
Typ des zu validierenden Feldwerts

Das Feld [CustomValidator] ermöglicht es Ihnen, Validierungen durchzuführen, die mit den von ASP.NET bereitgestellten Validierungskomponenten nicht möglich sind. Diese Validierung wird durch eine vom Entwickler geschriebene Funktion durchgeführt. Diese Funktion wird serverseitig ausgeführt. Da die Validierung auch clientseitig durchgeführt werden kann, muss der Entwickler möglicherweise eine JavaScript-Funktion entwickeln, die in das HTML-Dokument eingebunden wird. Die Eigenschaften lauten wie folgt:

ControlToValidate
Das Feld, dessen Wert von der Komponente validiert werden muss
EnableClientScript
Boolean – Wenn „true“, gibt dies an, dass der Inhalt des vorherigen Feldes auch auf der Clientseite validiert werden muss
ErrorMessage
Die Fehlermeldung, die die Komponente anzeigen soll, wenn ein Fehler erkannt wird
ClientValidationFunction
Die Funktion, die auf der Clientseite ausgeführt werden soll

Der Code der Seitenvorlage lautet wie folgt:

<%@ 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;trise</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>

Mit dem Attribut [OnServerValidate] der Komponente [CustomValidator] können Sie die Funktion angeben, die für die serverseitige Validierung zuständig ist. Im obigen Beispiel ruft die Komponente [CustomValidator1] die folgende Prozedur [CustomValidator1_ServerValidate_1] auf:

<%@ Page Language="VB" %>
<script runat="server">

    Sub CustomValidator1_ServerValidate_1(sender As Object, e As ServerValidateEventArgs)
        ' field [txtAutreDiplome] must be non-empty if [cmbDiplomes]=[other]
        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>
....

Eine Validierungsfunktion, die einer [CustomValidator]-Komponente zugeordnet ist, erhält zwei Parameter:

  • sender: das Objekt, das das Ereignis ausgelöst hat
  • e: das Ereignis. Die Prozedur muss die Eigenschaft [e.IsValid] auf „true“ setzen, wenn die validierten Daten korrekt sind, andernfalls auf „false“.

Hier werden die folgenden Prüfungen durchgeführt:

  • Die Dropdown-Liste [cmbDiplomes] darf nicht den Wert [?] enthalten. Dies wird von der Komponente [CompareValidator2] überprüft
  • Der Benutzer wählt einen Abschluss aus der Dropdown-Liste [cmbDiplomes] aus. Wenn sein Abschluss nicht in der Liste vorhanden ist, kann er die Option [Other] aus der Liste auswählen. Er muss dann seinen Abschluss in das Eingabefeld [txtAutreDiplome] eingeben. Wenn in [cmbDiplomes] die Option [Other] ausgewählt ist, darf das Feld [txtAutreDiplome] nicht leer sein. Wenn in [cmbDiplomes] weder [Other] noch [?] ausgewählt ist, muss das Feld [txtAutreDiplome] leer sein. Dies wird durch die mit der Komponente [CustomValidator1] verknüpfte Funktion überprüft.
  • Das Jahr, in dem der Abschluss erworben wurde, muss im Bereich [1900-2004] liegen. Dies wird durch [RangeValidator1] überprüft. Wenn der Benutzer das Feld jedoch leer lässt, wird die Validierungsfunktion von [RangeValidator1] nicht verwendet. Daher fügen wir die Komponente [RequiredFieldValidator2] zu [ ] hinzu, um das Vorhandensein von Inhalt zu prüfen. Dies ist eine allgemeine Regel. Der Inhalt eines Feldes wird nicht geprüft, wenn es leer ist. Der einzige Fall, in dem er geprüft wird, ist, wenn er mit einer [RequiredFieldValidator]-Komponente verknüpft ist.

Hier ist ein Beispiel für die Ausführung im [Mozilla]-Browser:

Image

Wenn wir den [IE6]-Browser verwenden, können wir eine clientseitige Validierungsfunktion für die Komponente [CustomValidator1] hinzufügen. Dazu verfügt diese Komponente über die folgenden Eigenschaften:

  • EnableClientScript=true
  • ClientValidationFunction=chkAutreDiplome

Die Funktion [chkAutreDiplome] lautet wie folgt:

<head>
    <meta http-equiv="pragma" content="no-cache" />
    <script language="javascript">
        function chkAutreDiplome(source,args){
            // vérifie la validité du champ txtAutreDiplome
            with(document.frmCandidature){
                diplome=cmbDiplomes.options[cmbDiplomes.selectedIndex].text;
                    args.IsValid= !(diplome=="[Autre]" && txtAutreDiplome.value=="")
                        && ! (diplome!="[Autre]" && diplome!="?" && txtAutreDiplome.value!="");
            }
        }
        </script>
</head>

Die Funktion [chkAutreDiplome] erhält dieselben zwei Parameter wie die Serverfunktion [CustomValidator1_ServerValidate_1]:

  • source: das Objekt, das das Ereignis ausgelöst hat
  • args: das Ereignis. Dieses verfügt über ein [IsValid]-Attribut, das von der Funktion auf „true“ gesetzt werden muss, wenn die validierten Daten korrekt sind. Bei einem Wert von „false“ wird die zugehörige Fehlermeldung an der Stelle der Validierungskomponente angezeigt, und das Formular wird nicht an den Server übermittelt.

8.2.5. RegularExpressionValidator

Wir erstellen die folgende Seite [regularexpressionvalidator1.aspx]:

Image

Nr.
name
Typ
Eigenschaften
Rolle
1
txtMel
Textfeld
EnableViewState=true
Eingabefeld
2
RequiredFieldValidator3
RequiredFieldValidator
Zu validierendes Steuerelement=txtMel
Anzeige=Dynamisch
Steuerelement-Validierungskomponente [1]
3
RegularExpressionValidator1
RegularExpressionValidator
Zu validierendes Steuerelement=txtMel
Anzeige=Dynamisch
Komponente zur Steuerelementvalidierung [1]
4
btnSend
Schaltfläche
EnableViewState=false
CausesValidation=true
[submit]-Schaltfläche

Hier möchten wir das Format einer E-Mail-Adresse validieren. Dazu verwenden wir eine [RegularExpressionValidator]-Komponente, mit der wir ein Feld anhand eines regulären Ausdrucks validieren können. Denken Sie daran, dass ein regulärer Ausdruck ein Muster aus Zeichen ist. Wir prüfen daher den Inhalt eines Feldes anhand eines Musters. Da der Inhalt eines Feldes nicht geprüft wird, wenn es leer ist, benötigen wir zusätzlich eine [RequiredFieldValidator]-Komponente, um sicherzustellen, dass die E-Mail-Adresse nicht leer ist. Die [RegularExpressionValidator]-Klasse verfügt über Eigenschaften, die denen der bereits behandelten Komponentenklassen ähneln:

ControlToValidate
Feld, dessen Wert von der Komponente validiert werden muss
EnableClientScript
boolescher Wert – wenn „true“, gibt dies an, dass der Inhalt des vorherigen Feldes ebenfalls auf der Clientseite validiert werden muss
ErrorMessage
die Fehlermeldung, die die Komponente anzeigen soll, wenn ein Fehler erkannt wird
RegularExpression
der reguläre Ausdruck, mit dem der Inhalt von [ControlToValidate] verglichen wird
Anzeige
Anzeigemodus: Statisch: Das Feld ist immer vorhanden, auch wenn keine Fehlermeldung angezeigt wird; Dynamisch: Das Feld ist nur vorhanden, wenn eine Fehlermeldung vorliegt; Keine: Die Fehlermeldung wird nicht angezeigt. Dieses Feld existiert auch für andere Komponenten, wurde jedoch bisher nicht verwendet.

Das reguläre Ausdrucksmuster für eine E-Mail-Adresse könnte wie folgt lauten: \s*[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+\s*

Eine E-Mail-Adresse hat die Form [champ1.champ2....@champA.champB...]. Vor dem @-Zeichen muss mindestens ein Feld und danach mindestens zwei Felder vorhanden sein. Diese Felder bestehen aus alphanumerischen Zeichen und dem Zeichen -. Ein alphanumerisches Zeichen wird durch das Symbol \w dargestellt. Die Sequenz [\w-] bedeutet das Zeichen \w oder das Zeichen -. Das +-Zeichen nach einer Sequenz S bedeutet, dass diese 1 oder mehrmals wiederholt werden kann; das *-Zeichen bedeutet, dass sie 0 oder mehrmals wiederholt werden kann; das ?-Zeichen bedeutet, dass sie 0 oder 1 Mal wiederholt werden kann.

Ein Feld in der E-Mail-Adresse entspricht dem Muster [\w-]+. Die Tatsache, dass vor dem @-Zeichen mindestens ein Feld und danach mindestens zwei Felder stehen müssen, die durch das .-Zeichen getrennt sind, entspricht dem Muster: [\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+. Wir können dem Benutzer erlauben, Leerzeichen vor und nach der Adresse einzufügen. Diese werden bei der Verarbeitung entfernt. Eine Folge von Leerzeichen, die leer sein kann, wird durch das Muster \s* dargestellt. Daher lautet der reguläre Ausdruck für die E-Mail-Adresse: \s*[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+\s*.

Der Code für das Seitenlayout lautet dann wie folgt:

<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

Aus ästhetischen Gründen möchten Sie Fehlermeldungen möglicherweise an einer einzigen Stelle zusammenfassen, wie im folgenden Beispiel [summaryvalidator1.aspx], in dem wir alle vorherigen Beispiele auf einer einzigen Seite zusammengefasst haben:

Image

Alle Validierungssteuerelemente verfügen über die folgenden Eigenschaften:

  • [Display=Dynamic], wodurch sichergestellt wird, dass die Steuerelemente keinen Platz auf der Seite beanspruchen, wenn ihre Fehlermeldung leer ist
  • [EnableClientScript=false], um die gesamte clientseitige Validierung zu deaktivieren
  • [Text=*]. Dies ist die Meldung, die im Fehlerfall angezeigt wird, während der Inhalt des Attributs [ErrorMessage] durch das unten gezeigte [ValidationSummary]-Steuerelement angezeigt wird.

Diese Steuerelemente werden in einer [Panel]-Komponente namens [vueFormulaire] platziert. In einer weiteren [Panel]-Komponente namens [vueErreurs] platzieren wir ein [ValidationSummary]-Steuerelement:

Image

Nr.
name
Typ
Eigenschaften
Rolle
1
ValidationSummary1
Validierungszusammenfassung
HeaderText=Die folgenden Fehler sind aufgetreten, EnableClientScript=false, ShowSummary=true
zeigt Fehler aus allen Validierungssteuerelementen auf der Seite an
2
lnkErrorsToForm
LinkButton
ValidationCauses=false
Link zurück zum Formular

Für den Link [lnkErrorsToForm] muss die Validierung nicht aktiviert werden, da dieser Link keine zu prüfenden Werte übermittelt.

Wenn alle Daten gültig sind, wird dem Benutzer die folgende [info]-Ansicht angezeigt:

Image

1

Nr.
Name
Typ
Eigenschaften
Rolle
1
lblInfo
Beschriftung
 
Informationsmeldung für den Benutzer

Der HTML-Code für diese Seite lautet wie folgt:

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

Hier sind einige Beispiele für die erzielten Ergebnisse. Wir senden die [Formular]-Ansicht ab, ohne Werte einzugeben:

Image

Die Schaltfläche [Absenden] liefert uns folgende Antwort:

Image

Die Ansicht [errors] wurde angezeigt. Wenn wir den Link verwenden, um zum Formular zurückzukehren, finden wir es in folgendem Zustand vor:

Image

Dies ist die Ansicht [form]. Beachten Sie das Zeichen [*] neben den fehlerhaften Daten. Dies ist das Feld [Text] der Validierungssteuerelemente, das angezeigt wurde. Wenn wir die Felder korrekt ausfüllen, erhalten wir die Ansicht [info]:

Image

Der Code für das Seitensteuerelement lautet wie folgt:

<%@ Page Language="VB" %>
<script runat="server">

    ' procedure executed when the page is loaded
    Sub page_Load(sender As Object, e As EventArgs)
        ' on the 1st request, we present the [form] view
        if not ispostback then
            afficheVues(true,false,false)
        end if
    end sub

    sub afficheVues(byval formulaireVisible as boolean, _
        erreursVisible as boolean, infosVisible as boolean)
        ' set of views
        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)
        ' displays the form view
        afficheVues(true,false,false)
        ' redo validity checks
        Page.validate
    End Sub

    Sub btnEnvoyer_Click(sender As Object, e As EventArgs)
        ' is the page valid?
        if not Page.IsValid then
            ' the [errors] view is displayed
            afficheVues(false,true,false)
        else
            ' otherwise the view [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>
  • In der Prozedur [Page_Load], die bei jeder Client-Anfrage ausgeführt wird, zeigen wir die Ansicht [form] an, während die anderen ausgeblendet werden. Dies geschieht nur bei der ersten Anfrage. Die Prozedur ruft eine Hilfsprozedur [displayViews] auf, an die wir drei boolesche Werte übergeben, um zu bestimmen, ob die drei Ansichten angezeigt werden sollen oder nicht.
  • Wenn die Prozedur [btnEnvoyer_Click] aufgerufen wird, wurde die Datenvalidierung bereits durchgeführt. Die Schaltfläche [btnEnvoyer] verfügt über die Eigenschaft [CausesValidation=true], die diese Datenvalidierung auslöst. Alle Validierungssteuerelemente wurden ausgeführt, und ihre Eigenschaft [IsValid] wurde gesetzt. Diese Eigenschaft gibt an, ob die vom Steuerelement validierten Daten gültig waren oder nicht. Darüber hinaus wurde auch die Eigenschaft [IsValid] der Seite selbst gesetzt. Sie ist nur dann [true], wenn die Eigenschaft [IsValid] aller Validierungssteuerelemente auf der Seite den Wert [true] hat. Daher beginnt die Prozedur [btnEnvoyer_Click] damit, zu prüfen, ob die Seite gültig ist oder nicht. Ist sie ungültig, wird die Ansicht [errors] angezeigt. Diese Ansicht enthält das Steuerelement [ValidationSummary], das die [ErrorMessage]-Attribute aller Steuerelemente auflistet (siehe Screenshot oben). Ist die Seite gültig, wird die Ansicht [info] zusammen mit einer Informationsmeldung angezeigt.
  • Die Prozedur [lnkErrorsToForm_Click] ist dafür zuständig, die Ansicht [form] in dem Zustand anzuzeigen, in dem sie validiert wurde. Da alle Eingabefelder in der Ansicht [form] die Eigenschaft [EnableViewState=true] haben, wird ihr Zustand automatisch neu generiert. Seltsamerweise wird der Zustand der Validierungskomponenten nicht wiederhergestellt. Man könnte erwarten, dass bei der Rückkehr zur Ansicht [errors] die ungültigen Steuerelemente ihr Feld [Text] anzeigen. Dies ist jedoch nicht der Fall. Wir haben daher die Datenvalidierung mithilfe der [Page.Validate]-Methode der Seite erzwungen. Dies muss erfolgen, sobald das [formView]-Panel sichtbar gemacht wurde. Es gibt somit insgesamt zwei Validierungen. Dies sollte in der Praxis vermieden werden. An dieser Stelle ermöglichte uns das Beispiel, neue Konzepte zur Seitenvalidierung vorzustellen.

8.3. ListControl-Komponenten und Datenbindung

Einige der behandelten Serverkomponenten ermöglichen es Ihnen, eine Liste von Werten anzuzeigen (DropDownList, ListBox). Andere, die wir noch nicht behandelt haben, ermöglichen es Ihnen, mehrere Listen von Werten in HTML-Tabellen anzuzeigen. Bei allen ist es möglich, die Werte in den Listen programmgesteuert einzeln mit der entsprechenden Komponente zu verknüpfen. Es ist auch möglich, komplexere Objekte mit diesen Komponenten zu verknüpfen, wie z. B. Objekte vom Typ [Array], [ArrayList], [DataSet], [HashTable] usw., was den Code vereinfacht, der die Daten mit der Komponente verknüpft. Diese Verknüpfung wird als Datenbindung bezeichnet.

Alle von der Klasse [ListControl] abgeleiteten Komponenten können mit einer Datenliste verknüpft werden. Dazu gehören die Komponenten [DropDownList], [ListBox], [CheckButtonList] und [RadioButtonList]. Jede dieser Komponenten kann an eine Datenquelle gebunden werden. Dies kann verschiedene Formen annehmen: [Array], [ArrayList], [DataTable], [DataSet], [HashTable], … im Allgemeinen ein Objekt, das eine der Schnittstellen IEnumerable, ICollection oder IListSource implementiert. Wir werden hier nur einige davon vorstellen. Ein [DataSet]-Objekt ist eine Darstellung einer relationalen Datenbank. Es handelt sich also um eine Menge von Tabellen, die durch Beziehungen miteinander verknüpft sind. Das [DataTable]-Objekt repräsentiert eine solche Tabelle. Die Datenquelle legt die Eigenschaften [Text] und [Value] jedes [Item] im [ListControl]-Objekt fest. Wenn T der Wert von [Text] und V der Wert von [Value] ist, lautet der für jedes Element von [ListControl] generierte HTML-Tag wie folgt:

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

Eine [ListControl]-Komponente wird über die folgenden Eigenschaften mit einer Datenquelle verknüpft:

DataSource
eine Datenquelle [Array], [ArrayList], [DataTable], [DataSet], [HashTable], ...
DataMember
Wenn die Datenquelle ein [DataSet] ist, stellt dies den Namen der Tabelle dar, die als Datenquelle verwendet werden soll. Die eigentliche Datenquelle ist dann eine Tabelle.
DataTextField
Wenn die Datenquelle eine Tabelle ist ([DataTable], [DataSet]), stellt dies den Namen der Tabellenspalte dar, die Werte für das [Text]-Feld der [ListControl]-Elemente bereitstellt
DataValueField
Wenn die Datenquelle eine Tabelle ist ([DataTable], [DataSet]), steht dies für den Namen der Tabellenspalte, die Werte für das [Value]-Feld der [ListControl]-Elemente bereitstellt

Das Binden einer [ListControl]-Komponente an eine Datenquelle initialisiert die Komponente nicht mit den Werten aus der Datenquelle. Dies erfolgt durch die Operation [ListControl].DataBind.

Je nach Art der Datenquelle erfolgt die Bindung an eine [ListControl]-Komponente auf unterschiedliche Weise:

Array A
[ListControl].DataSource=A
Die Felder [Text] und [Value] der [ListControl]-Elemente enthalten die Werte der Elemente in A
ArrayList AL
[ListControl].DataSource=AL
Die Felder [Text] und [Value] der [ListControl]-Elemente enthalten die Werte der Elemente in AL
DataTable DT
[ListControl].DataSource = DT, [ListControl].DataTextField = "col1", [ListControl].DataValueField = "col2"
wobei col1 und col2 zwei Spalten in der DT-Tabelle sind. Die Felder [Text] und [Value] der [ListControl]-Elemente enthalten die Werte der Spalten col1 und col2 in der DT-Tabelle
DataSet DS
[ListControl].DataSource=DS, [ListControl].DataSource="table", wobei „table“ der Name einer der Tabellen in DS ist.
[ListControl].DataTextField = „col1“, [ListControl].DataValueField = „col2“, wobei col1 und col2 zwei Spalten der Tabelle „table“ sind. Die Felder [Text] und [Value] der [ListControl]-Elemente enthalten die Werte der Spalten col1 und col2 der Tabelle „table“
HashTable HT
[ListControl].DataSource = HT, [ListControl].DataTextField = "key", [ListControl].DataValueField = "value", wobei [key] und [value] die Schlüssel bzw. Werte von HT sind.

Wir wenden diese Informationen auf das folgende Beispiel [databind1.aspx] an:

Hier haben wir fünf Datenbindungen, jede mit vier Steuerelementen der Typen [DropDownList], [ListBox], [CheckBoxList] und [RadioButtonList]. Die fünf untersuchten Bindungen unterscheiden sich in ihren Datenquellen:

Bindung
Datenquelle
1
Array
2
ArrayList
3
DataTable
4
DataSet
5
HashTable

8.3.1. Code zur Darstellung der Komponente

Der Präsentationscode für die Steuerelemente in Bindung 1 lautet wie folgt:

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

Die Bindung für die Bindungen 2 bis 5 ist bis auf die Bindungsnummer identisch.

8.3.2. Bindung an eine Array-Datenquelle

Die Bindung der vier oben genannten [ListBox]-Steuerelemente erfolgt wie folgt in der [Page_Load]-Prozedur des Steuerelementcodes:

' global data
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

' procedure executed when the page is loaded
Sub page_Load(sender As Object, e As EventArgs)
    if not IsPostBack then
      ' create the data sources to be linked to the components
      createDataSources
      ' link to an array [Array]
      bindToArray
      ' link to a list [ArrayList]
      bindToArrayList
      ' link to a table [DataTable]
      bindToDataTable
      ' link to a data group [DataSet]
      bindToDataSet
      ' link to a dictionary [HashTable]
      bindToHashTable
    end if
End Sub

sub createDataSources
  ' creates data sources to be linked to components
  ' arraylist
  dim i as integer
  for i=0 to textes.length-1
    myDataListe.add(textes(i))
  next
  ' datatable
  ' we define its two columns
  myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
  myDataTable.Columns.Add("texte",Type.GetType("System.String"))
  ' fill the table
  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 - a single table
  myDataSet.Tables.Add(myDataTable)
  ' hashtable
  for i=0 to textes.length-1
    myHashTable.add(valeurs(i),textes(i))
  next
end sub

' panel connection
sub bindToArray
        ' association with components
        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
        ' item selection
        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

Die Steuerelemente werden hier nur bei der ersten Anforderung an die Daten gebunden. Danach behalten die Steuerelemente ihre Elemente über den [VIEWSTATE]-Mechanismus bei. Die Bindung erfolgt in der Prozedur [bindToArray]. Da die Datenquelle vom Typ [Array] ist, wird nur das Feld [DataSource] der [ListControl]-Komponenten initialisiert. Das Steuerelement wird mithilfe der Methode [ListControl].DataBind mit Werten aus der zugehörigen Datenquelle gefüllt. Erst dann verfügen die [ListControl]-Objekte über Elemente. Sie können dann einige davon auswählen.

8.3.3. Bindung an eine ArrayList-Datenquelle

Die Datenquelle [myDataList] wird in der Prozedur [createDataSources] initialisiert:

' global data
dim textes() as string={"un","deux","trois","quatre"}
dim myDataListe as new ArrayList
..

' procedure executed when the page is loaded
Sub page_Load(sender As Object, e As EventArgs)
    if not IsPostBack then
      ' create the data sources to be linked to the components
      createDataSources
...
    end if
End Sub

sub createDataSources
  ' creates data sources to be linked to components
  ' arraylist
  dim i as integer
  for i=0 to textes.length-1
    myDataListe.add(textes(i))
  next
...
end sub

Die Bindung der vier [ListBox]-Steuerelemente in Bindung 2 erfolgt wie folgt in der Prozedur [bindToArrayList] des Steuerelementcodes:

' liaison arraylist
sub bindToArrayList
        ' l'association aux composants
        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
        ' la sélection des éléments
        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

Da die Datenquelle vom Typ [ArrayList] ist, wird nur das Feld [DataSource] der [ListControl]-Komponenten initialisiert.

8.3.4. Datenquelle vom Typ DataTable

Die Datenquelle [myDataTable] wird in der Prozedur [createDataSources] initialisiert:

' global data
dim textes() as string={"un","deux","trois","quatre"}
dim myDataTable as new DataTable("table1")
...

' procedure executed when the page is loaded
Sub page_Load(sender As Object, e As EventArgs)
    if not IsPostBack then
      ' create the data sources to be linked to the components
      createDataSources
...
    end if
End Sub

sub createDataSources
  ' creates data sources to be linked to components
...
  ' datatable
  ' we define its two columns
  myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
  myDataTable.Columns.Add("texte",Type.GetType("System.String"))
  ' fill the table
  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

Wir beginnen mit der Erstellung eines [DataTable]-Objekts mit zwei Spalten: [id] und [text]. Die Spalte [id] füllt das Feld [Value] der [ListControl]-Elemente, und die Spalte [text] füllt deren [Text]-Felder. Die [DataTable] mit zwei Spalten wird wie folgt erstellt:

  myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
  myDataTable.Columns.Add("texte",Type.GetType("System.String"))

Wir erstellen somit eine Tabelle mit zwei Spalten:

  • Die erste, „id“ genannt, ist vom Typ Integer
  • die zweite, „text“ genannt, ist vom Typ String

Nachdem die Tabellenstruktur nun erstellt wurde, können wir sie mit dem folgenden Code füllen:

  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

Die Spalte [id] enthält die Ganzzahlen [0,1,..,n], während die Spalte [text] die Werte aus dem Array [data] enthält. Sobald dies geschehen ist, wird die Tabelle [dataList] gefüllt. Die Bindung der vier [ListBox]-Steuerelemente in Bindung 3 erfolgt wie folgt in der Prozedur [bindToDataTable] des Steuerelementcodes:

sub bindToDataTable
        ' l'association aux composants
        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
        ' la sélection des éléments
        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

Jede [ListControl]-Komponente wird an die Datenquelle [myDataTable] gebunden, indem für jede Komponente Folgendes festgelegt wird:

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

Die Tabelle [myDataTable] ist die Datenquelle. Die Spalte [id] dieser Tabelle füllt die [Value]-Felder der Komponentenelemente, während die Spalte [text] deren [Text]-Felder füllt.

8.3.5. Datenquelle vom Typ DataSet

Die Datenquelle [myDataSet] wird in der Prozedur [createDataSources] initialisiert:

' global data
dim myDataTable as new DataTable("table1")
dim myDataSet as new DataSet
...


' procedure executed when the page is loaded
Sub page_Load(sender As Object, e As EventArgs)
    if not IsPostBack then
      ' create the data sources to be linked to the components
      createDataSources
...
    end if
End Sub

sub createDataSources
  ' creates data sources to be linked to components
  ' dataset - a single table
  myDataSet.Tables.Add(myDataTable)
...
end sub

Ein [DataSet]-Objekt stellt eine Sammlung von [DataTable]-Tabellen dar. Wir fügen die zuvor erstellte [myDataTable] zum [DataSet] hinzu. Die Bindung der vier [ListBox]-Steuerelemente in Bindung 4 erfolgt wie folgt in der Prozedur [bindToDataSet] des Steuerelementcodes:

sub bindToDataSet
        ' l'association aux composants
        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
        ' la sélection des éléments
        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

Jede [ListControl]-Komponente ist wie folgt mit der Datenquelle verknüpft:

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

Der Datensatz [myDataSet] ist die Datenquelle. Da er mehrere Tabellen enthalten kann, geben wir in [DataMember] den Namen der zu verwendenden Tabelle an. Die Spalte [id] dieser Tabelle füllt die [Value]-Felder der Komponentenelemente, während die Spalte [text] deren [Text]-Felder füllt.

8.3.6. HashTable-Datenquelle

Die Datenquelle [myHashTable] wird in der Prozedur [createDataSources] initialisiert:

' global data
dim textes() as string={"un","deux","trois","quatre"}
dim valeurs() as string={"1","2","3","4"}
dim myHashTable as new HashTable
...

' procedure executed when the page is loaded
Sub page_Load(sender As Object, e As EventArgs)
    if not IsPostBack then
      ' create the data sources to be linked to the components
      createDataSources
...
    end if
End Sub

sub createDataSources
  ' creates data sources to be linked to components
...
  ' hashtable
  for i=0 to textes.length-1
    myHashTable.add(valeurs(i),textes(i))
  next
end sub

Das Wörterbuch [myHashTable] kann als Tabelle mit zwei Spalten betrachtet werden, die „key“ und „value“ heißen. Die Spalte [key] stellt die Schlüssel des Wörterbuchs dar, und die Spalte [value] stellt die ihnen zugeordneten Werte dar. Hier besteht die Spalte [key] aus dem Inhalt des Arrays [values], und die Spalte [value] besteht aus dem Inhalt des Arrays [texts]. Die Bindung dieser Quelle an die Steuerelemente erfolgt in der Prozedur [bindToHashTable]:

sub bindToHashTable
        ' l'association aux composants
        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
        ' la sélection des éléments
        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

Für jede Komponente wird die Bindung mit den folgenden Anweisungen hergestellt:

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

Die Datenquelle ist das Wörterbuch [myHashTable]. Die Steuerelementwerte werden von der Spalte [key] des Wörterbuchs bereitgestellt, der Text von der Spalte [value]. Die Wörterbuchelemente werden in der Reihenfolge der Schlüssel in die Steuerelemente eingefügt, die anfangs zufällig ist.

8.3.7. Anweisungen zum Importieren von Namespaces

Eine Reihe von Namespaces wird automatisch in eine ASP.NET-Seite importiert. Dies gilt jedoch nicht für „System.Data“, wo sich die Klassen [DataTable] und [DataSet] befinden. Daher muss diese Klasse importiert werden. Dies geschieht wie folgt:

<%@ Page Language="VB" %>
<%@ import Namespace="System.Data" %>
<script runat="server">
...
</script>
<html>
...
</html>

8.4. DataGrid-Komponente und Datenbindung

Die [DataGrid]-Komponente ermöglicht es Ihnen, Daten in Tabellenform darzustellen, geht jedoch weit über eine einfache Anzeige hinaus:

  • sie bietet die Möglichkeit, die „visuelle Darstellung“ der Tabelle präzise zu konfigurieren
  • sie ermöglicht es Ihnen, die Datenquelle zu aktualisieren

Die [DataGrid]-Komponente ist sowohl leistungsstark als auch komplex. Wir werden sie Schritt für Schritt vorstellen.

8.4.1. Anzeigen einer Datenquelle vom Typ Array, ArrayList, DataTable oder DataSet

Mit der [DataGrid]-Komponente können Sie Datenquellen vom Typ [Array], [ArrayList], [DataTable] und [DataSet] in einer HTML-Tabelle anzeigen. Für diese vier Datentypen verknüpfen Sie die Quelle einfach mit der [DataSource]-Eigenschaft der [DataGrid]-Komponente:

DataSource
eine Datenquelle [Array], [ArrayList], [DataTable], [DataSet], ...
DataMember
Wenn die Datenquelle ein [DataSet] ist, stellt dies den Namen der Tabelle dar, die als Datenquelle verwendet werden soll. Die eigentliche Datenquelle ist dann eine Tabelle. Wenn dieses Feld leer gelassen wird, werden alle Tabellen im [DataSet] angezeigt.

Wir stellen nun die Seite [datagrid1.aspx] vor, die ein [DataGrid] zeigt, das mit vier verschiedenen Datenquellen verknüpft ist:

Image

Die Seite enthält vier [DataGrid]-Komponenten, die wie folgt mit [WebMatrix] erstellt wurden. Wir ziehen die Komponente an ihre Position auf der Registerkarte [Design]:

Image

Anschließend wird eine generische HTML-Tabelle gezeichnet. Die Eigenschaften eines [DataGrid] können zur Entwurfszeit definiert werden. Genau das tun wir hier für seine Formatierungseigenschaften. Dazu wählen wir das zu konfigurierende [DataGrid] aus. Seine Eigenschaften werden in einem Fenster unten rechts angezeigt:

Image

Wir verwenden die beiden oben genannten Links. Der Link [Property Generator] bietet Zugriff auf die wichtigsten Eigenschaften des [DataGrid]:

Image

Wir deaktivieren die Option [Show Header] für die vier [DataGrid]-Komponenten und speichern die Seite. Der andere Link, [Auto Format], ermöglicht es Ihnen, aus mehreren Stilen für die anzuzeigende HTML-Tabelle auszuwählen:

Image

Wir wählen [color i] für [DataGrid] #i aus. Diese Designentscheidungen spiegeln sich im Darstellungscode der Seite wider:

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

Im Entwurfsmodus haben wir lediglich Formatierungseigenschaften festgelegt. Im Steuerelementcode verknüpfen wir die Daten mit den vier Komponenten:

<%@ Page Language="VB" %>
<%@ import Namespace="system.data" %>
<script runat="server">

    ' global data
        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

        ' procedure executed when the page is loaded
        Sub page_Load(sender As Object, e As EventArgs)
            if not IsPostBack then
              ' create the data sources to be linked to the components
              createDataSources
              ' link to an array [Array]
              bindToArray
              ' link to a list [ArrayList]
              bindToArrayList
              ' link to a table [DataTable]
              bindToDataTable
              ' link to a data group [DataSet]
              bindToDataSet
            end if
        End Sub

        sub createDataSources
          ' creates data sources to be linked to components
          ' arraylist
          dim i as integer
          for i=0 to textes1.length-1
            myDataListe.add(textes1(i))
          next
          ' datatable
          ' we define its two columns
          myDataTable.Columns.Add("id",Type.GetType("System.Int32"))
          myDataTable.Columns.Add("texte1",Type.GetType("System.String"))
          myDataTable.Columns.Add("texte2",Type.GetType("System.String"))
          ' fill the table
          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 - a single table
          myDataSet.Tables.Add(myDataTable)
        end sub

        ' panel connection
        sub bindToArray
          with DataGrid1
            .DataSource=textes1
            .DataBind
          end with
        end sub

        ' arraylist link
        sub bindToArrayList
          with DataGrid2
            .DataSource=myDataListe
            .DataBind
          end with
        end sub

        ' datatable link
        sub bindToDataTable
          with DataGrid3
            .DataSource=myDataTable
            .DataBind
          end with
        end sub

        ' dataset link
        sub bindToDataSet
          with DataGrid4
            .DataSource=myDataSet
            .DataBind
          end with
        end sub

</script>
<html>
...
</html>

Der Code ähnelt stark dem im vorherigen Beispiel, daher werden wir nicht speziell darauf eingehen. Beachten Sie jedoch, wie die Datenbindung erfolgt. Für jedes der vier Steuerelemente reicht die folgende Sequenz aus:

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

wobei [Datenquelle] vom Typ [Array], [ArrayList], [DataTable] oder [DataSet] ist. Wir sehen also, dass dies ein leistungsstarkes Werkzeug zur Darstellung von Daten in Tabellen ist:

  • Das Layout wird während der Entwurfsphase mit [WebMatrix] oder einer anderen IDE erstellt
  • Die Datenbindung erfolgt im Code. Mit [WebMatrix] kann dies zur Entwurfszeit erfolgen, wenn es sich bei der Datenquelle um eine SQL Server-Datenbank oder eine Access-Datenbank handelt. In diesem Fall wird eine [SqlDataSource]- oder [AccessDataSource]-Komponente auf dem Formular platziert. Diese Komponente kann zur Entwurfszeit mit der physischen Datenquelle verknüpft werden, je nach Bedarf entweder mit einer SQL Server-Datenbank oder einer Access-Datenbank. Wenn Sie der [DataSource]-Eigenschaft einer [DataGrid]-Komponente ein mit einer physischen Quelle verknüpftes [SqlDataSource]- oder [AccessDataSource]-Objekt zuweisen, werden die tatsächlichen Daten im Entwurfsmodus in der [DataGrid]-Komponente angezeigt.

8.5. ViewState von Datenlistenkomponenten

Hier möchten wir den [VIEWSTATE]-Mechanismus für Datenlistenkomponenten hervorheben. Sie haben die Wahl zwischen zwei Methoden, um den Zustand einer Datenlistenkomponente zwischen zwei Client-Anfragen beizubehalten:

  • das [VIEWSTATE]-Attribut auf „true“ setzen
  • das [VIEWSTATE]-Attribut auf „false“ setzen und die Datenquelle in der Sitzung speichern, damit die Komponente bei der nächsten Anfrage mit dieser Quelle verknüpft werden kann.

Das folgende Beispiel veranschaulicht bestimmte Aspekte des [VIEWSTATE]-Mechanismus für Datencontainer. Bei der ersten Anfrage des Clients zeigt die Anwendung die folgende Ansicht an:

Nr.
Name
Typ
Eigenschaften
Rolle
1
DataGrid1
DataGrid
EnableViewState=true
zeigt eine Datenquelle S
2
DataGrid2
DataGrid
EnableViewState=true
zeigt dieselbe Datenquelle S an wie [DataGrid1]
3
Button1
Button
EnableViewState=false
[Senden]-Schaltfläche

Die Komponente [DataGrid1] wird durch den [VIEWSTATE]-Mechanismus verwaltet. Wir möchten feststellen, ob dieser Mechanismus, der die Anzeige von [DataGrid1] bei jeder Anfrage neu generiert, auch dessen Datenquelle neu generiert. Dazu wird die Datenquelle mit der Komponente [DataGrid2] verknüpft. Deren Generierung bei jeder Anfrage erfolgt über eine explizite Bindung an die Datenquelle von [DataGrid1]. Ihr Attribut [EnableViewState] ist ebenfalls auf [true] gesetzt.

Der Präsentationscode der Anwendung [main.aspx] lautet wie folgt:


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

Der Controller [main.aspx.vb] sieht wie folgt aus:


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
        ' on the 1st query, the data source is defined and linked to the 1st datagrid
        If Not IsPostBack Then
            'define data source
            With DataGrid1
                .DataSource = createDataSource()
                .DataBind()
            End With
        End If
        ' for each query, datagrid2 is linked to the source of the 1st datagrid
        With Datagrid2
            .DataSource = DataGrid1.DataSource
            .DataBind()
        End With
    End Sub
 
    Private Function createDataSource() As DataTable
        ' initialize the data source
        Dim thèmes As New DataTable
        ' columns
        With thèmes.Columns
            .Add("id", GetType(System.Int32))
            .Add("thème", GetType(System.String))
            .Add("description", GetType(System.String))
        End With
        ' column id will be primary key
        thèmes.Constraints.Add("cléprimaire", thèmes.Columns("id"), True)
        ' lines
        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

Die Methode [createDataSource] erstellt eine Datenquelle S vom Typ [DataTable]. Wir werden nicht näher auf den Code eingehen, da er nicht im Mittelpunkt dieses Beispiels steht. Dies ist die Methode, mit der die beiden für uns interessanten [DataGrid]-Komponenten erstellt werden:

  • Die [DataGrid1]-Komponente wird einmalig während der ersten Abfrage an die Tabelle S gebunden. Danach ist sie nicht mehr gebunden.
  • Die Komponente [DataGrid2] wird bei jeder neuen Abfrage an die Quelle [DataGrid1.DataSource] gebunden.

Bei der ersten Abfrage erhalten wir folgende Ansicht:

Image

Logischerweise zeigen beide Komponenten die Datenquelle an, mit der sie verknüpft wurden. Wir verwenden die Schaltfläche [Submit], um einen [PostBack] an den Server auszulösen. Die resultierende Ansicht sieht dann wie folgt aus:

Image

Wir stellen fest, dass die [DataGrid1]-Komponente ihren Wert beibehalten hat, die [DataGrid2]-Komponente jedoch nicht. Erklärung:

  • Noch bevor die [Page_Load]-Prozedur startet, haben die Objekte [DataGrid1] und [DataGrid2] aufgrund des [viewstate]-Mechanismus die Werte abgerufen, die sie bei der vorherigen Anfrage hatten. Tatsächlich ist bei beiden die Eigenschaft [EnableViewState] auf [true] gesetzt.
  • Die [Page_Load]-Prozedur wird ausgeführt. Da es sich um einen [PostBack]-Vorgang handelt, wird die [DataGrid1]-Komponente durch [Page_Load] nicht verändert (siehe Code). Daher behält sie den über [viewstate] abgerufenen Wert bei. Dies zeigt der obige Bildschirm.
  • Die Komponente [DataGrid2] hingegen ist über [DataBind] an die Datenquelle [DataGrid1.DataSource] gebunden. Sie wird daher neu aufgebaut, und der Wert, den sie gerade über [viewstate] abgerufen hatte, geht verloren. Es wäre daher hier von Vorteil gewesen, wenn ihre Eigenschaft [EnableViewState] auf [false] gesetzt worden wäre, um unnötige Zustandsverwaltung zu vermeiden. Der obige Bildschirm zeigt, dass [DataGrid2] an eine leere Quelle gebunden wurde. Da es sich bei dieser Quelle um [DataGrid1.DataSource] handelt, können wir daraus schließen, dass der [viewstate]-Mechanismus zwar die Anzeige der [DataGrid1]-Komponente erfolgreich wiederherstellt, jedoch nicht deren Eigenschaften wie [DataSource].

Was können wir aus diesem Beispiel schließen? Sie sollten es vermeiden, die Eigenschaft [EnableViewState] eines Datencontainers auf [true] zu setzen, wenn dieser bei jeder Anfrage an eine Datenquelle gebunden (DataBind) werden muss. Es gibt jedoch bestimmte Fälle, in denen selbst in diesem Szenario die Eigenschaft [EnableViewState] des Containers auf [true] gesetzt bleiben muss; andernfalls werden Ereignisse, die Sie behandeln möchten, nicht ausgelöst. Ein Beispiel hierfür werden wir später sehen.

Häufig ändert sich die Datenquelle eines Datencontainers im Laufe der Anfragen. Daher muss der Container bei jeder Anfrage an die Datenquelle gebunden werden. Es ist üblich, dass die Datenquelle auf die Sitzung beschränkt ist, damit Anfragen darauf zugreifen können. Unser zweites Beispiel veranschaulicht diesen Mechanismus. Die Anwendung stellt nur eine einzige Ansicht bereit:

Nr.
name
Typ
Eigenschaften
Rolle
1
DataGrid1
DataGrid
EnableViewState=true
zeigt eine Datenquelle S
2
DataGrid2
DataGrid
EnableViewState=false
zeigt dieselbe Datenquelle S an wie [DataGrid1]
3
Button1
Button
EnableViewState=false
[submit]-Schaltfläche
4
lblInfo1
lblInfo2
lblInfo3
Beschriftung
EnableViewState=false
Informationstext

Die Komponente [DataGrid1] wird nur bei der ersten Anfrage an die Daten gebunden. Dank des [viewstate]-Mechanismus behält sie ihren Wert über mehrere Anfragen hinweg bei. Die Komponente [DataGrid2] wird bei jeder Anfrage an eine Datenquelle gebunden, zu der bei jeder neuen Anfrage ein Element hinzugefügt wird. Daher muss die Komponente [DataGrid2] bei jeder Anfrage gebunden (DataBind) werden. Wir haben daher ihr Attribut [EnableViewState] wie zuvor empfohlen auf [false] gesetzt. So erhalten wir bei der zweiten Anfrage (über die Schaltfläche [Submit]) die folgende Antwort:

Image

Die [DataGrid1]-Komponente hat ihren ursprünglichen Wert beibehalten. Die [DataGrid2]-Komponente enthält ein weiteres Element. Die drei Werte [1,2,2] stellen die Abfragenummer dar. Wir können sehen, dass einer der Werte falsch ist. Wir werden versuchen zu verstehen, warum.

Der Präsentationscode der Anwendung [main.aspx] lautet wie folgt:


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

Der Controller-Code [main.aspx.vb] lautet wie folgt:


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
        ' on the 1st query, the data source is defined and linked to the 1st datagrid
        If Not IsPostBack Then
            'define data source
            dtThèmes = createDataSource()
            With DataGrid1
                .DataSource = dtThèmes
                .DataBind()
            End With
            ' store information in the session
            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
        ' a new theme is added to each query
        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)
        'links datagrid2 with the data source
        With Datagrid2
            .DataSource = dtThèmes
            .DataBind()
        End With
        ' info no. of requests
        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
        ' store some information in the session
        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

Wir haben den Code für die Methode [createDataSource] nicht wiedergegeben. Er entspricht dem der vorherigen Anwendung, mit der Ausnahme, dass der Quellcode nur drei Zeilen umfasst. Schauen wir uns zunächst an, wie die Datenquelle und die beiden Container verwaltet werden:


    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' on the 1st query, the data source is defined and linked to the 1st datagrid
        If Not IsPostBack Then
            'define data source
            dtThèmes = createDataSource()
            With DataGrid1
                .DataSource = dtThèmes
                .DataBind()
            End With
            ' store information in the session
            Session("source") = dtThèmes
...
        End If
        ' a new theme is added to each query
        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)
        'links datagrid2 with the data source
        With Datagrid2
            .DataSource = dtThèmes
            .DataBind()
        End With
...
    End Sub

Die Komponente [DataGrid1] wird nur während der ersten Abfrage (nicht bei IsPostBack) an die Datenquelle S gebunden. Anschließend wird sie in die Sitzung aufgenommen. Danach wird sie nicht erneut dort abgelegt. Die in der Sitzung abgelegte Datenquelle S wird bei jeder Anfrage abgerufen, und eine neue Zeile wird hinzugefügt. Die Komponente [DataGrid2] wird bei jeder Anfrage explizit an die Quelle S gebunden. Deshalb erhöht sich ihr Inhalt bei jeder Anfrage um eine Zeile. Beachten Sie, dass die Quelle S nach einer Änderung ihres Inhalts nicht explizit über eine Operation wieder in die Sitzung zurückgelegt wird:

            Session("source") = S

Warum? Zu Beginn einer Abfrage enthält die Sitzung ein Session("source")-Objekt vom Typ [DataTable], das der Datenquelle entspricht, wie sie bei der letzten Abfrage vorlag. Nennen wir das Session("source")-Objekt S. Wenn wir schreiben:


        dtThèmes = CType(Session("source"), DataTable)

sind [dtThèmes] und [S] zwei Verweise auf dasselbe [DataTable]-Objekt. Wenn also der Code in [Page_Load] ein Element zur Tabelle hinzufügt, auf die [dtThèmes] verweist, fügt er es gleichzeitig zur Tabelle hinzu, auf die [S] verweist. Am Ende der Ausführung der Seite werden alle Objekte in der Sitzung gespeichert, einschließlich des Objekts Session("source"), d. h. S, d. h. [dtThemes]. Somit wird tatsächlich der neue Inhalt der Datenquelle gespeichert. Es war nicht notwendig zu schreiben:

            Session("source") = dtThèmes

um diese Speicherung durchzuführen, da Session("source") bereits gleich [dtThèmes] ist. Dies gilt nicht mehr, wenn die in der Sitzung platzierten Daten keine Objekte sind, wie beispielsweise die Strukturen [Integer, Float, ...]. Dies wird durch die Verwaltung des Abfragezählers veranschaulicht:


    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
        ' on the 1st query, the data source is defined and linked to the 1st datagrid
....
            ' store information in the session
            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
        ' a new theme is added to each query
....
        ' info no. of requests
        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
        ' store some information in the session
        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

Wir speichern die Anforderungszähler in drei Elementen:

  • numRequest1 und numRequest2 vom Typ [Integer] – [Integer] ist keine Klasse, sondern eine Struktur
  • numRequest3 vom Typ [integer] – [integer] ist eine für diesen Zweck definierte Klasse

Wenn wir schreiben:


        numRequête1 = CType(Session("numRequête1"), Integer)
..
        numRequête2 = CType(Session("numRequête2"), Integer)
..
        numRequête3 = CType(Session("numRequête3"), entier)
..
  • Die Struktur [Session("numRequest1")] wird in [numRequest1] kopiert. Wenn also das Element [numRequest1] geändert wird, wird das Element [Session("numRequest1")] selbst nicht
  • Das Gleiche gilt für [Session("numRequest2")] und [numRequest2]
  • Die Elemente [Session("numRequête3")] und [numRequête3] sind beide Verweise auf dasselbe Objekt vom Typ [integer]. Das referenzierte Objekt kann über jeden der beiden Verweise geändert werden.

Daraus lässt sich schließen, dass es unnötig ist, Folgendes zu schreiben:

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

um den neuen Wert von [numRequest3] in der Sitzung zu speichern. Stattdessen sollten Sie schreiben:

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

um die neuen Werte der Strukturen [numRequête1] und [numRequête2] zu speichern. Wir tun dies nur für [numRequête2], was erklärt, warum im Screenshot nach der zweiten Abfrage der Zähler [numRequête1] falsch ist.

Es ist daher zu beachten, dass Daten, sobald sie einer Sitzung hinzugefügt wurden, nicht wiederholt hinzugefügt werden müssen, wenn sie durch ein Objekt repräsentiert werden. In anderen Fällen müssen sie hinzugefügt werden, wenn sie geändert wurden.

8.6. Anzeigen einer Datenliste mithilfe eines paginierten und sortierten DataGrid

Die [DataGrid]-Komponente ermöglicht es Ihnen, den Inhalt eines [DataSet] anzuzeigen. Bisher haben wir unsere [DataSets] immer „von Hand“ erstellt. Dieses Mal verwenden wir ein [DataSet] aus einer Datenbank. Wir erstellen die folgende MVC-Anwendung:

Die drei Ansichten werden als Container in den Präsentationscode des [main.aspx]-Controllers eingebunden. Daher besteht diese Anwendung aus einer einzigen Seite, [main.aspx].

8.6.1. Die Business-Klassen

Die Klasse [products] bietet Zugriff auf die folgende ACCESS-Datenbank:

Image

Der Code für die Klasse [products] lautet wie folgt:


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)
            ' save the connection string
            Me.chaineConnexionOLEDB = chaineConnexionOLEDB
        End Sub
 
        Public Function getDataSet(ByVal commande As String) As DataSet
            ' create a DataAdapter object to read data from source OLEDB
            Dim adaptateur As New OleDbDataAdapter(commande, chaineConnexionOLEDB)
            ' create a memory image of the select result
            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
            ' we return the result
            Return contenu
        End Function
    End Class
End Namespace

Die ACCESS-Datenbank wird über einen OLEDB-Treiber verwaltet. Wir übergeben dem Konstruktor der Klasse [products] die Verbindungszeichenfolge, den OLEDB-Treiber und die zu verwaltende Datenbank. Die Klasse [products] verfügt über eine Methode [getDataSet], die ein [DataSet] zurückgibt, das durch die Ausführung einer SQL-[select]-Abfrage erhalten wird, deren Text als Parameter übergeben wird. Während der Methode finden mehrere Vorgänge statt: Herstellen der Verbindung zur Datenbank, Ausführen der [select]-Abfrage und Schließen der Verbindung. All dies kann eine Ausnahme auslösen, die hier behandelt wird. Ein Testprogramm könnte wie folgt aussehen:

Option Explicit On 
Option Strict On

' namespaces
Imports System
Imports System.Data
Imports Microsoft.VisualBasic

Namespace st.istia.univangers.fr

    ' test pg
    Module testproduits
        Sub Main(ByVal arguments() As String)
            ' displays the contents of a product table
            ' the table is in a ACCESS database whose pg receives the file name
            Const syntaxe1 As String = "pg bdACCESS"

            ' checking program parameters
            If arguments.Length <> 1 Then
                ' error msg
                Console.Error.WriteLine(syntaxe1)
                ' end
                Environment.Exit(1)
            End If

            ' prepare the connection chain
            Dim chaineConnexion As String = "Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=" + arguments(0)

            ' creation of a product object
            Dim objProduits As produits = New produits(chaineConnexion)

            ' retrieve the product table from a dataset
            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

            ' display its contents
            Dim lignes As DataRowCollection = contenu.Tables(0).Rows
            For i As Integer = 0 To lignes.Count - 1
                ' table line 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

Die verschiedenen Komponenten dieser Anwendung werden wie folgt kompiliert:

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. Die Ansichten

Nachdem wir nun die Datenzugriffsklasse haben, schreiben wir die Controller und Ansichten für diese Webanwendung. Schauen wir uns zunächst an, wie das funktioniert. Die erste Ansicht sieht wie folgt aus:

Nr.
name
Typ
Eigenschaften
Rolle
1
txtSelect
Textfeld
EnableViewState=true
Eingabefeld für Select-Abfrage
2
RequiredFieldValidator1
RequiredFieldValidator
EnableViewState=false
prüft auf das Vorhandensein von 1
3
txtPages
TextBox
EnableViewState=true
Eingabefeld – gibt die Anzahl der Datenzeilen an, die pro Ergebnisseite angezeigt werden sollen
4
RequiredFieldValidator2
RequiredFieldValidator
EnableViewState=false
prüft auf das Vorhandensein von 3
5
RangeValidator1
RangeValidator
EnableViewState=false
prüft, ob (3) im Bereich [1,30] liegt
6
btnExecute
Schaltfläche
EnableViewState=false
[submit]-Schaltfläche

Wir nennen diese Ansicht die [form]-Ansicht. Sie ermöglicht es dem Benutzer, eine SQL-SELECT-Abfrage in der Datenbank [products.mdb] auszuführen. Durch die Ausführung der Abfrage wird eine neue Ansicht erstellt:

Nr.
Name
Typ
Eigenschaften
Rolle
1
lblSelect
Beschriftung
EnableViewState=false
Informationsfeld
2
rdAscending
rdDescending
RadioButton
EnableViewState=false
GroupName=rdSort
ermöglicht die Auswahl einer Sortierreihenfolge
3
DataGrid1
DataGrid
EnableViewState=true
AllowPaging=true
Sortierung zulassen=true
Tabelle mit den Ergebnissen der Auswahl
4
lnkResults
LinkButton
EnableViewState=false
[submit]-Schaltfläche

Wir nennen diese Ansicht die [results]-Ansicht. Sie enthält das [DataGrid], das die Ergebnisse der SQL-SELECT-Anweisung anzeigt. Der Benutzer kann bei seiner Abfrage einen Fehler machen. Einige Fehler werden ihm dank Validierungssteuerelementen in der [errors]-Ansicht gemeldet.

Image

Andere Fehler werden ihm über die [errors]-Ansicht gemeldet:

Image

Die Ansicht [errors] wird angezeigt:

Nr.
Name
Typ
Eigenschaften
Rolle
1
HTMLErrors
Variable
 
HTML-Code, der zur Anzeige von Fehlern erforderlich ist
3
lnkForm2
LinkButton
EnableViewState=false
[submit]-Schaltfläche

Die drei Ansichten der Anwendung sind drei verschiedene Container (Panels) innerhalb derselben Seite [main.aspx]. Der Präsentationscode lautet wie folgt:


<%@ 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. Konfigurieren des DataGrid

Sehen wir uns die Paginierung der [DataGrid]-Komponente, eine Funktion, auf die wir hier zum ersten Mal stoßen, einmal genauer an. Im obigen Code wird diese Paginierung durch die folgenden Attribute gesteuert:


                    <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"
aktiviert die Paginierung
PageSize="4"
vier Datenzeilen pro Seite
NextPageText="Weiter"
Der Linktext, um zur nächsten Seite der Datenquelle zu gelangen
PrevPageText="Zurück"
Der Linktext, um zur vorherigen Seite der Datenquelle zu gelangen

Diese Informationen können direkt in den Attributen des <asp:datagrid>-Tags eingegeben werden. Sie können auch [WebMatrix] verwenden. Klicken Sie im Eigenschaftenfenster von [DataGrid] auf den Link [Property Generator]:

Image

Der folgende Assistent wird angezeigt:

Image

Wählen Sie die Option [Pagination] aus:

Image

Oben sehen wir die Werte der Paginierungsattribute für das [DataGrid] im Präsentationscode.

Darüber hinaus ermöglichen wir die Sortierung der Daten in einer der [DataGrid]-Spalten. Hierfür gibt es mehrere Möglichkeiten. Eine davon besteht darin, die Eigenschaft [AllowSorting=true] im Eigenschaftenfenster von [DataGrid] festzulegen. Sie können auch den Eigenschaftsgenerator verwenden. Unabhängig von der gewählten Methode führt dies dazu, dass das Attribut [AllowSorting=true] im <asp:DataGrid>-Tag der Komponente vorhanden ist:


                    <asp:DataGrid id="DataGrid1" runat="server" ... AllowPaging="True" PageSize="4" AllowSorting="True">

8.6.4. Die Controller

Der Controller [global.asax, global.asax.vb] sieht wie folgt aus:

[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)
        ' create a product object
        Dim objProduits As produits
        Try
            objProduits = New produits(ConfigurationSettings.AppSettings("OLEDBStringConnection"))
            ' put the object in the application
            Application("objProduits") = objProduits
            ' no error
            Application("erreur") = False
        Catch ex As Exception
            'there has been an error, we note it in the application
            Application("erreur") = True
            Application("message") = ex.Message
        End Try
    End Sub
End Class

Beim Start der Anwendung (Application_Start) erstellen wir ein [products]-Objekt und speichern es in der Anwendung, damit es für alle Anfragen aller Clients verfügbar ist. Tritt während dieser Erstellung eine Ausnahme auf, wird diese in der Anwendung protokolliert. Die Prozedur [Application_Start] wird nur einmal ausgeführt. Danach ist der Controller [global.asax] nicht mehr beteiligt. Der Controller [main.aspx.vb] übernimmt dann den Rest:


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
 
    ' components 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
    ' data page
    Protected erreursHTML As String
 
    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' check for application errors
        If CType(Application("erreur"), Boolean) Then
            ' the application has not initialized correctly
            Dim erreurs As New ArrayList
            erreurs.Add("Application momentanément indisponible (" + CType(Application("message"), String) + ")")
            afficheErreurs(erreurs, False)
            Exit Sub
        End If
        '1st request
        If Not IsPostBack Then
            ' the empty form is displayed
            afficheFormulaire()
        End If
    End Sub
 
    Private Sub afficheErreurs(ByVal erreurs As ArrayList, ByVal afficheLien As Boolean)
        ' displays the error view
        erreursHTML = ""
        For i As Integer = 0 To erreurs.Count - 1
            erreursHTML += "<li>" + erreurs(i).ToString + "</li>" + ControlChars.CrLf
        Next
        lnkErreurs.Visible = afficheLien
        ' the [errors] view is displayed
        vueErreurs.Visible = True
        vueFormulaire.Visible = False
        vueRésultats.Visible = False
    End Sub
 
    Private Sub afficheFormulaire()
        ' the [form] view is displayed
        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)
        ' initialize controls
        lblSelect.Text = sqlTexte
        With DataGrid1
            .DataSource = données
            .PageSize = CType(txtPages.Text, Integer)
            .CurrentPageIndex = 0
            .DataBind()
        End With
        ' the [results] view is displayed
        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
        ' valid page?
        If Not Page.IsValid Then
            afficheFormulaire()
            Exit Sub
        End If
 
        ' execute query SELECT customer
        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
        ' all's well - the results are in
        afficheRésultats(txtSelect.Text.Trim, données)
        ' put the data in the session
        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
        ' set of views
        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
        ' change page
        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
        ' sort the dataview
        Dim données As DataView = CType(Session("données"), DataView)
        données.Sort = e.SortExpression + " " + CType(IIf(rdCroissant.Checked, "asc", "desc"), String)
        ' we display it
        With DataGrid1
            .CurrentPageIndex = 0
            .DataSource = données
            .DataBind()
        End With
    End Sub
End Class

Wenn die Seite geladen wird [Page_Load], prüfen wir zunächst, ob die Anwendung korrekt initialisiert werden konnte. Ist dies nicht der Fall, zeigen wir die Ansicht [errors] ohne Link zurück zum Formular an, da dieser Link dann überflüssig ist. Tatsächlich kann nur die Ansicht [errors] angezeigt werden, wenn die Anwendung nicht korrekt initialisiert werden konnte. Andernfalls zeigen wir die Ansicht [form] an, sofern dies die erste Anfrage des Clients ist. Im Übrigen überlassen wir es dem Leser, den Code zu verstehen. Wir konzentrieren uns nur auf drei Prozeduren: die Prozedur [btnExecute_Click], die ausgeführt wird, wenn der Benutzer die Ausführung der in der Ansicht [form] eingegebenen SQL-Abfrage anfordert, die Prozedur [DataGrid1_PageIndexChanged], die ausgeführt wird, wenn der Benutzer die Links [Next] und [Previous] im [DataGrid] verwendet, sowie die Prozedur [DataGrid1_SortCommand], die ausgeführt wird, wenn der Benutzer auf eine Spaltenüberschrift klickt, um die Daten in dieser Reihenfolge zu sortieren. Die Sortierreihenfolge – aufsteigend oder absteigend – wird durch die beiden Sortier-Optionsfelder bestimmt.

In der Prozedur [btnExécuter_Click] prüfen wir daher zunächst, ob die Seite gültig ist oder nicht. Wenn die Prozedur [btnExécuter_Click] ausgeführt wird, sind die Prüfungen in Bezug auf die verschiedenen Validierungssteuerelemente der Seite bereits durchgeführt worden. Für jedes Validierungssteuerelement wurden zwei Attribute festgelegt:

IsValid
wird auf „true“ gesetzt, wenn die überprüften Daten gültig sind, andernfalls auf „false“
ErrorMessage
die Fehlermeldung, wenn die überprüften Daten ungültig sind

Für die Seite selbst wurde ein [IsValid]-Attribut gesetzt. Es ist nur dann „true“, wenn bei allen Validierungssteuerelementen das [IsValid]-Attribut auf „true“ gesetzt ist. Ist dies nicht der Fall, muss die Ansicht [form] angezeigt werden. Diese Ansicht enthält die Validierungssteuerelemente, die ihr Attribut [errorMessage] anzeigen. Ist die Seite gültig, verwenden wir das von [Application_Start] erstellte Objekt [products], um das [DataSet] abzurufen, das der Ausführung der SQL-SELECT-Abfrage entspricht. Wir konvertieren dies in ein [DataView]-Objekt:


        Dim données As DataView
        Try
            données = CType(Application("objProduits"), produits).getDataSet(txtSelect.Text.Trim).Tables(0).DefaultView
...

Wir hätten einfach mit dem [DataSet] arbeiten und schreiben können:


        Dim données As DataSet
        Try
            données = CType(Application("objProduits"), produits).getDataSet(txtSelect.Text.Trim)
...

Ein [DataSet]-Objekt ist im Wesentlichen eine Sammlung von Tabellen, die durch Beziehungen miteinander verknüpft sind. In unserer konkreten Anwendung enthält das aus der Klasse [products] abgerufene [DataSet] nur eine einzige Tabelle, nämlich diejenige, die aus der [select]-Anweisung resultiert. Eine Tabelle kann sortiert werden, ein [DataSet] hingegen nicht; wir sind jedoch daran interessiert, die abgerufenen Daten zu sortieren. Um mit der Ergebnistabelle aus der [select]-Anweisung zu arbeiten, hätten wir schreiben können:


        Dim données As DataTable
        Try
            données = CType(Application("objProduits"), produits).getDataSet(txtSelect.Text.Trim).Tables(0)
...

Das [DataTable]-Objekt stellt zwar eine Datenbanktabelle dar, verfügt jedoch nicht über eine Sortiermethode. Dazu benötigen Sie eine Ansicht der Tabelle. Eine Ansicht ist ein Objekt vom Typ [DataView]. Mithilfe von Filtern können Sie verschiedene Ansichten derselben Tabelle erstellen. Eine Tabelle verfügt über eine Standardansicht, bei der keine Filter definiert sind. Sie repräsentiert daher die gesamte Tabelle. Diese Standardansicht wird über [DataTable.DefaultView] abgerufen. Sie können eine Ansicht mithilfe ihrer [sort]-Eigenschaft sortieren, auf die wir später noch eingehen werden.

Wenn das Abrufen des [DataSet] aus der Klasse [products] erfolgreich ist, wird die Ansicht [results] angezeigt; andernfalls wird die Ansicht [errors] angezeigt. Die Ansicht [results] wird über die Prozedur [displayResults] angezeigt, an die zwei Parameter übergeben werden:

  • den Text, der in das [lblSelect]-Label eingefügt werden soll
  • die [DataView], die an [DataGrid1] gebunden werden soll

Dieses Beispiel veranschaulicht die große Flexibilität der [DataGrid]-Komponente. Sie kann die Struktur der [DataView] erkennen, an die sie gebunden ist, und sich daran anpassen. Schließlich speichert die Prozedur [btnExécuter_Click] die soeben erhaltene [DataView] in der Sitzung des Benutzers, damit sie verfügbar ist, wenn der Benutzer andere Seiten aus derselben [DataView] anfordert.

Die Prozedur [DataGrid1_PageIndexChanged] wird ausgeführt, wenn der Benutzer auf die Links [Next] und [Previous] im [DataGrid] klickt. Sie erhält zwei Parameter:


    Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.) Handles DataGrid1.PageIndexChanged
source
das Objekt, das das Ereignis ausgelöst hat – in diesem Fall einer der Links [Weiter] oder [Zurück]
e
Informationen zum Ereignis. Die Eigenschaft e.NewPageIndex ist die Seitenzahl, die als Antwort auf die Anfrage des Clients angezeigt werden soll

Der vollständige Code für den Ereignishandler lautet wie folgt:


    Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) Handles DataGrid1.PageIndexChanged
        ' change page
        With DataGrid1
            .CurrentPageIndex = e.NewPageIndex
            .DataSource = CType(Session("données"), DataView)
            .DataBind()
        End With
    End Sub

Die [DataGrid]-Komponente verfügt über ein [CurrentPageIndex]-Attribut, das die Seitenzahl angibt, die gerade angezeigt wird oder angezeigt werden soll. Wir weisen diesem Attribut den [NewPageIndex]-Wert des Parameters [e] zu. Das [DataGrid] wird dann an das [DataView] gebunden, das von der Prozedur [btnExécuter_Click] in der Sitzung gespeichert wurde.

Man könnte sich fragen, ob das [DataGrid] das Attribut [EnableViewState=true] benötigt, da sein Inhalt bei jedem Neuladen der Seite vom Code berechnet wird. Man könnte meinen, dass dies nicht der Fall ist. Wenn das [DataGrid] jedoch das Attribut [EnableViewState=false] hat, stellen wir fest, dass das Ereignis [DataGrid1.PageIndexChanged] nie ausgelöst wird. Aus diesem Grund haben wir [EnableViewState=true] beibehalten. Wir wissen, dass dies dazu führt, dass der Inhalt des [DataGrid] im versteckten Feld [__VIEWSTATE] der Seite gespeichert wird. Dies kann die Seite erheblich verlangsamen, wenn das [DataGrid] groß ist. Sollte dies ein Problem darstellen, können Sie die Paginierung selbst verwalten, ohne die automatische Paginierung des [DataGrid] zu verwenden.

Die Prozedur [DataGrid1_SortCommand] wird ausgeführt, wenn der Benutzer auf die Überschrift einer der vom [DataGrid] angezeigten Spalten klickt, um die Sortierung der Daten in der Reihenfolge dieser Spalte anzufordern. Sie erhält zwei Parameter:


    Private Sub DataGrid1_SortCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles DataGrid1.SortCommand
source
das Objekt, das das Ereignis ausgelöst hat – in diesem Fall einer der Links [Weiter] oder [Zurück]
e
Informationen zum Ereignis. Die Eigenschaft [e.SortExpression] ist der Name der Spalte, auf die zum Sortieren geklickt wurde

Der vollständige Code für den Ereignishandler lautet wie folgt:


    Private Sub DataGrid1_SortCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles DataGrid1.SortCommand
        ' sort the dataview
        Dim données As DataView = CType(Session("données"), DataView)
        données.Sort = e.SortExpression + " " + CType(IIf(rdCroissant.Checked, "asc", "desc"), String)
        ' we display it
        With DataGrid1
            .CurrentPageIndex = 0
            .DataSource = données
            .DataBind()
        End With
    End Sub

Wir rufen die [DataView] ab, die vom [DataGrid] in der aktuellen Sitzung angezeigt wird. Sie wurde dort von der Prozedur [btnExécuter_Click] platziert. Die [DataView]-Komponente verfügt über eine [Sort]-Eigenschaft, der wir den Sortierausdruck zuweisen. Dies folgt der Syntax [select ... order by expr1, expr2, ...], wobei jedem [expr] das Schlüsselwort [asc] für aufsteigende Sortierung oder [desc] für absteigende Sortierung folgen kann. Der hier verwendete [order by]-Ausdruck lautet [order by column asc/desc]. Die Eigenschaft [e.SortExpression] liefert uns den Namen der [DataGrid]-Spalte, auf die zum Sortieren geklickt wurde. Die Zeichenfolge [asc/desc] wird basierend auf den Werten der Optionsfelder in der Gruppe [rdTri] festgelegt. Sobald der Sortierausdruck der [DataView] festgelegt ist, wird das [DataGrid] daran gebunden. Wir positionieren das [DataGrid] auf seiner ersten Seite.

8.7. DataList-Komponente und Datenbindung

Wir konzentrieren uns nun auf die [DataList]-Komponente. Sie bietet mehr Formatierungsoptionen als das [DataGrid], ist jedoch weniger flexibel. Daher kann sie sich nicht automatisch an die Datenquelle anpassen, mit der sie verknüpft ist. Diese Anpassung muss bei Bedarf über Code erfolgen. Wenn die Struktur der Datenquelle im Voraus bekannt ist, bietet diese Komponente Formatierungsoptionen, die sie gegenüber dem [DataGrid] vorteilhaft machen können.

8.7.1. Anwendung

Um die Verwendung der [DataList] zu veranschaulichen, erstellen wir eine MVC-Anwendung, die der vorherigen ähnelt:

Die drei Ansichten werden als Container in den Präsentationscode des Controllers [main.aspx] eingebunden. Daher besteht diese Anwendung aus einer einzigen Seite [main.aspx].

8.7.2. Geschäftsklassen

Die Klasse [products] ist dieselbe wie zuvor.

8.7.3. Die Ansichten

Wenn der Benutzer seine erste Anfrage an die Anwendung stellt, sieht er die folgende Ansicht [results1]:

Nr.
Name
Typ
Eigenschaften
Rolle
1
RadioButton1
RadioButton2
RadioButton
EnableViewState=false
ermöglicht es Ihnen, einen von zwei [DataList]-Stilen auszuwählen
2
btnChanger
Button
EnableViewState=false
[submit]-Schaltfläche
3
DataList1
DataList
EnableViewState=true
Anzeigefeld der Datenliste

Wenn der Benutzer Stil Nr. 2 auswählt, sieht er die folgende Ansicht [results2]:

Nr.
Name
Typ
Eigenschaften
Rolle
1
DataList2
DataList
EnableViewState=true
Anzeigefeld der Datenliste

Die Ansicht [Fehler] weist auf ein Problem beim Zugriff auf die Datenquelle hin:

Nr.
Name
Typ
Eigenschaften
Rolle
1
HTMLErrors
Variable
 
HTML-Code, der zur Anzeige von Fehlern erforderlich ist

Die drei Ansichten der Anwendung sind drei verschiedene Container (Panels) innerhalb derselben Seite [main.aspx]. Der Präsentationscode lautet wie folgt:


<%@ 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("nom") %>
                            <br>
                            prix :
                            <%# databinder.eval(Container.DataItem,"prix","{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("nom") %>
                            , prix :
                            <%# databinder.eval(Container.DataItem,"prix","{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. Konfigurieren von [DataList]-Komponenten

Werfen wir einen Blick auf die verschiedenen Attribute einer [DataList]-Komponente. Es gibt viele davon, und wir werden hier nur eine kleine Auswahl behandeln. Sie können innerhalb einer [DataList] bis zu sieben Anzeigevorlagen definieren:

HeaderTemplate
[DataList]-Kopfzeilenvorlage
ItemTemplate
Vorlage für die Zeilen, in denen die Elemente der zugehörigen Datenliste angezeigt werden. Nur diese Vorlage ist erforderlich.
AlternatingItemTemplate
Um visuell zwischen aufeinanderfolgenden angezeigten Elementen zu unterscheiden, können zwei Vorlagen verwendet werden: ItemTemplate für Element n, AlternatingItemTemplate für Element n+1
SelectedItemTemplate
Vorlage für das ausgewählte Element in der [DataList]
SeparatorTemplate
Vorlage für das Trennzeichen zwischen zwei Elementen in der [DataList]
EditItemTemplate
Eine [DataList] ermöglicht es Ihnen, die angezeigten Werte zu bearbeiten. [EditItemTemplate] ist die Vorlage für ein Element in der [DataList], das sich derzeit im „Bearbeitungsmodus“ befindet
FooterTemplate
Vorlage für die Fußzeile der [DataList]

Die Komponente [DataList1] wurde mit [WebMatrix] erstellt. In ihrem Eigenschaftenfenster wurde der Link [AutoFormat] ausgewählt:

1234567

Oben generiert das Schema [Color 5] eine [DataList] mit Stilen für die folgenden Vorlagen: HeaderTemplate (1), ItemTemplate (2, 6), AlternatingTemplate (3, 5), SelectedItemTemplate (4), FooterTemplate (7). Der generierte Code lautet wie folgt:


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

Es wurden keine Vorlagen definiert. Das müssen wir selbst tun. Wir definieren die folgenden Vorlagen:

HeaderTemplate

                        <HeaderTemplate>
                            Inhalt der Tabelle [list] in der Datenbank [products]
                        </HeaderTemplate>
ItemTemplate

                        <ItemTemplate>
                            name:
                            <%# Container.DataItem("name") %>
                            
                            Preis:
                            <%# DataBinder.Eval(Container.DataItem,"price","{0:C}") %>
                            
                        </ItemTemplate>

Beachten Sie, dass die Vorlage [ItemTemplate] dazu dient, die Elemente aus der mit [DataList] verknüpften Datenquelle anzuzeigen. Diese Datenquelle ist eine Sammlung von Datenzeilen, von denen jede einen oder mehrere Werte enthält. Die aktuelle Zeile in der Datenquelle wird durch das Objekt [Container.DataItem] dargestellt. Eine solche Zeile enthält Spalten. [Container.DataItem("col1")] ist der Wert der Spalte „col1“ in der aktuellen Zeile. Um diesen Wert in den Präsentationscode einzufügen, schreiben wir <%# Container.DataItem("col") %>. Manchmal möchten wir ein Element der aktuellen Zeile in einem speziellen Format anzeigen. Hier möchten wir die Spalte „price“ der aktuellen Zeile in Euro anzeigen. Wir verwenden die Funktion [DataBinder.Eval], die drei Parameter benötigt:

  • die aktuelle Zeile [Container.DataItem]
  • den Namen der zu formatierenden Spalte
  • die Formatierungszeichenfolge in der Form {0:format}, wobei [format] eines der von der Methode [string.format] akzeptierten Formate ist.

Somit zeigt der Code <%# DataBinder.Eval(Container.DataItem,"price",{0:C}) %> die Spalte [price] der aktuellen Zeile im Währungsformat an (Format C = Währung).

Wir erhalten somit eine [DataList], die wie folgt aussieht:

Image

Oben wurden die Daten mit vier Datenelementen pro Zeile angeordnet. Dies wird mithilfe der folgenden [DataList]-Attribute erreicht:

RepeatDirection
Horizontal
RepeatColumns
gewünschte Anzahl von Spalten

Letztendlich entspricht der Code für [DataList1] dem im obigen Codeausschnitt dargestellten. Wir überlassen es dem Leser, den Präsentationscode für [DataList2] zu studieren. Wie bei der [DataGrid]-Komponente können die meisten [DataList]-Eigenschaften mithilfe eines [WebMatrix]-Assistenten festgelegt werden. Verwenden Sie dazu den Link [Property Generator] im Eigenschaftenfenster von [DataList]:

8.7.5. Die Controller

Der Controller [global.asax, global.asax.vb] sieht wie folgt aus:

[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)
        ' create a product object
        Try
            Dim données As DataSet = New produits(ConfigurationSettings.AppSettings("OLEDBStringConnection")).getDataSet("select * from LISTE")
            ' put the object in the application
            Application("données") = données
            ' no error
            Application("erreur") = False
        Catch ex As Exception
            'there has been an error, we note it in the application
            Application("erreur") = True
            Application("message") = ex.Message
        End Try
    End Sub
End Class

Beim Start der Anwendung (Application_Start) erstellen wir ein [dataset] aus der Business-Klasse [products] und fügen es der Anwendung hinzu, damit es für alle Anfragen aller Clients verfügbar ist. Tritt während dieser Erstellung eine Ausnahme auf, wird diese in der Anwendung protokolliert. Die Prozedur [Application_Start] wird nur einmal ausgeführt. Danach ist der Controller [global.asax] nicht mehr beteiligt. Der Controller [main.aspx.vb] übernimmt dann die Arbeit:


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
 
    ' components 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
    ' data page
    Protected erreursHTML As String
 
    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' check for application errors
        If CType(Application("erreur"), Boolean) Then
            ' the application has not initialized correctly
            Dim erreurs As New ArrayList
            erreurs.Add("Application momentanément indisponible (" + CType(Application("message"), String) + ")")
            afficheErreurs(erreurs, False)
            Exit Sub
        End If
        '1st request
        If Not IsPostBack Then
            ' initialize controls
            With DataList1
                .DataSource = CType(Application("données"), DataSet)
                .DataBind()
            End With
            With DataList2
                .DataSource = CType(Application("données"), DataSet)
                .DataBind()
            End With
            ' the empty form is displayed
            afficheRésultats(True, False)
        End If
    End Sub
 
    Private Sub afficheErreurs(ByVal erreurs As ArrayList, ByVal afficheLien As Boolean)
        ' displays the error view
        erreursHTML = ""
        For i As Integer = 0 To erreurs.Count - 1
            erreursHTML += "<li>" + erreurs(i).ToString + "</li>" + ControlChars.CrLf
        Next
        ' the [errors] view is displayed
        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)
        ' the [results] view is displayed
        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
        ' change the style
        afficheRésultats(RadioButton1.Checked, RadioButton2.Checked)
    End Sub
End Class

8.8. Repeater-Komponente und Datenbindung

Mit der [Repeater]-Komponente können Sie den HTML-Code für jedes Element in einer Datenliste wiederholen. Angenommen, wir möchten eine Liste von Fehlern im folgenden Format anzeigen:

Image

Wir sind bereits auf dieses Problem gestoßen und haben es gelöst, indem wir eine Variable in den Präsentationscode in der Form <% =erreursHTML %> eingefügt haben, wobei der Wert von erreursHTML vom Controller berechnet wird. Dieser Wert enthält HTML-Code, insbesondere den einer Liste. Der Nachteil ist, dass man, wenn man die Darstellung dieser HTML-Liste ändern möchte, in den Controller-Bereich gehen muss, was der Trennung von Controller und Präsentation zuwiderläuft. Die [Repeater]-Komponente bietet eine Lösung. Wie bei der [DataList] können wir <HeaderTemplate> für die Kopfzeile, <ItemTemplate> für das aktuelle Element in der Datenliste und <FooterTemplate> für das Ende der Daten definieren. Hier könnten wir die folgende Definition für die [Repeater]-Komponente haben:

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

Beachten Sie, dass [Container.DataItem] eine Datenzeile darstellt, wenn die Datenquelle mehrere Spalten enthält. Es stellt einen einzelnen Datenpunkt dar, wenn die Quelle nur eine Spalte enthält. Dies ist hier der Fall. Wir erstellen beispielsweise die folgende Anwendung:

Image

Der Code für das Seitenlayout lautet wie folgt:

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

Der Steuerungscode lautet wie folgt:

<%@ Page Language="VB" %>
<script runat="server">

    ' procedure executed when the page is loaded
    Sub page_Load(sender As Object, e As EventArgs)
        if not IsPostBack then
          ' create a data source
          with Repeater1
              .DataSource=createDataSource
              .DataBind
          end with
        end if
    End Sub

    function createDataSource as ArrayList
      ' create an 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>

Bei der ersten Anfrage des Clients verknüpfen wir ein [ArrayList]-Objekt mit der [Repeater]-Komponente, das eine Liste von Fehlern darstellen soll.

8.9. Anwendung

Hier greifen wir eine Anwendung wieder auf, die zuvor mit serverseitigen Komponenten implementiert wurde. Die Anwendung ermöglicht es Benutzern, Steuerberechnungen zu simulieren. Sie stützt sich auf eine [impot]-Klasse, auf die wir hier nicht näher eingehen werden. Diese Klasse benötigt Daten, die sie aus einer OLEDB-Datenquelle abruft. Für dieses Beispiel verwenden wir eine ACCESS-Datenquelle. In dieser Version führen wir die folgenden neuen Funktionen ein:

  • die Verwendung von Validierungskomponenten zur Überprüfung der Datenvalidität
  • die Verwendung von Serverkomponenten, die mit Datenquellen verknüpft sind, zur Anzeige der Ergebnisse

8.9.1. Die MVC-Struktur der Anwendung

Die MVC-Struktur der Anwendung sieht wie folgt aus:

Die drei Ansichten werden als Container in den Präsentationscode des Controllers [main.aspx] eingebunden. Daher verfügt diese Anwendung über eine einzige Seite [main.aspx].

8.9.2. Die Ansichten der Anwendung

Die Ansicht [form] ist das Formular zur Eingabe von Informationen, die zur Berechnung der Steuer eines Benutzers verwendet werden:

Image

Der Benutzer füllt das Formular aus:

Image

Er nutzt die Schaltfläche [Submit], um eine Steuerberechnung anzufordern. Er sieht die folgende Ansicht [simulations]:

Image

Über den obigen Link kehrt er zum Formular zurück. Er findet es in dem Zustand vor, in dem er es verlassen hat. Es können Eingabefehler auftreten:

Image

Diese werden in der Ansicht [Formular] markiert:

Image

Der Benutzer korrigiert dann seine Fehler. Er kann neue Simulationen durchführen:

Image

Daraufhin wird die Ansicht [Simulationen] mit einer zusätzlichen Simulation angezeigt:

Image

Wenn die Datenquelle nicht verfügbar ist, wird der Benutzer schließlich in der Ansicht [Fehler] benachrichtigt:

Image

8.9.2.1. Der Präsentationscode

Denken Sie daran, dass die Seite [main.aspx] alle Ansichten zusammenführt. Es handelt sich um ein einziges Formular mit drei Containern:

  • [panelform] für die Ansicht [Formular]
  • [panelerrors] für die Ansicht [Fehler]
  • [panelsimulations] für die Ansicht [simulations]

Wir werden nun die Komponenten dieser drei Container näher erläutern. Der [panelform]-Container hat die folgende visuelle Darstellung:

Nr.
Name
Typ
Eigenschaften
Rolle
0
Panelform
Panel
 
Formularansicht
1
rdJa
rNein
Optionsfeld
GroupName=rdmarie
Optionsfelder
2
cvMarie
Benutzerdefinierter Validator
Fehlermeldung=Sie haben Ihren Familienstand nicht angegeben
EnableClientScript=false
prüft, ob das Client-Programm den erwarteten Familienstand gesendet hat
3
txtChildren
TextBox
 
Anzahl der Kinder
4
rfvChildren
RequiredFieldValidator
Fehlermeldung=Geben Sie die Anzahl der Kinder ein
ControlToValidate=txtChildren
prüft, ob das Feld [txtChildren] nicht leer ist
5
rvChildren
Bereichsprüfung
ErrorMessage=Geben Sie eine Zahl zwischen 1 und 30 ein
ControlToValidate=txtChildren
prüft, ob das Feld [txtChildren] im Bereich [1,30] liegt
6
txtSalary
Textfeld
EnableViewState=true
Jahresgehalt
7
rfvSalary
Erforderliches Feld-Validator
Zu validierendes Steuerelement=txtSalary
Fehlermeldung=Geben Sie Ihren Gehaltsbetrag ein
prüft, ob das Feld [txtSalary] nicht leer ist
8
revSalary
RegularExpressionValidator
ControlToValidate=txtSalary
Fehlermeldung=Ungültiges Gehalt
Regulärer Ausdruck=\s*\d+\s*
prüft, ob das Feld [txtSalary] eine Folge von Ziffern ist
9
btnCalculate
Schaltfläche
ValidationTriggers=true
[submit]-Schaltfläche im Formular – startet die Steuerberechnung
10
btnClear
Schaltfläche
Validation=false
[Absenden]-Schaltfläche im Formular – löscht das Formular

Vielleicht überrascht Sie die Überprüfung [cvMarié], die sicherstellt, dass der Benutzer eines der beiden Optionsfelder ausgewählt hat. Dies ist notwendig, da nicht mit Sicherheit festgestellt werden kann, ob der Benutzer das vom Server gesendete Formular verwendet. Da dies nicht garantiert werden kann, müssen wir alle übermittelten Parameter überprüfen. Beachten Sie auch das Attribut [CausesValidation=false] der Schaltfläche [btnEffacer]. Wenn der Benutzer auf diese Schaltfläche klickt, sollten die übermittelten Daten nicht überprüft werden, da sie ignoriert werden.

Alle Komponenten im Container verfügen über die Eigenschaft [EnableViewState=false], mit Ausnahme von [panelForm, txtEnfants, txtSalaire, rdOui, rdNon]. Der Container [panelerreurs] hat die folgende visuelle Darstellung:

Nr.
Name
Typ
Eigenschaften
Rolle
0
Fehlerfeld
Panel
EnableViewState=false
Fehleransicht
1
rptErrors
Repeater
EnableViewState=false
zeigt eine Liste von Fehlern an

Die Komponente [rptErrors] ist wie folgt definiert:


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

Die mit der Komponente [rptErrors] verknüpfte Datenliste ist ein [ArrayList]-Objekt, das eine Liste von Fehlermeldungen enthält. Wie oben gezeigt, bezieht sich <%# Container.Dataitem%> daher auf die aktuelle Fehlermeldung. Der Container [panelsimulations] hat die folgende visuelle Darstellung:

Nr.
Name
Typ
Eigenschaften
Rolle
0
Panel-Simulationen
Panel
EnableViewState=false
Simulationsansicht
2
lnkForm2
LinkButton
EnableViewState=false
Link zum Formular
1
dgSimulations
DataGrid
EnableViewState=false
verantwortlich für die Anzeige der in einem [DataTable]-Objekt platzierten Simulationen

Die Komponente [dgSimulations] wird in [Webmatrix] wie folgt konfiguriert. Im Eigenschaftenfenster von [dgSimulations] wählen wir den Link [AutoFormat] und wählen das Schema [Color 3]:

Anschließend wählen wir, immer noch im Eigenschaftenfenster von [dgSimulations], den Link [Property Generator] aus. Wir legen fest, dass die Spaltenüberschriften angezeigt werden sollen:

Image

Wir wählen die Option [Columns], um die vier Spalten des [DataGrid] zu definieren:

Image

Wir deaktivieren die Option [Spalten automatisch erstellen...]. Wir werden die vier Spalten des [DataGrid] selbst definieren. Dies wird mit einem [DataTable]-Objekt verknüpft, das vier Spalten mit den Namen „verheiratet“, „Kinder“, „Gehalt“ und „Steuer“ enthält. Um eine Spalte im [DataGrid] zu erstellen, wählen wir die Option [Related Columns] und verwenden die Schaltfläche zum Erstellen, wie oben gezeigt. Eine Spalte wird erstellt, und wir können ihre Eigenschaften definieren. Die erste Spalte der Simulationstabelle enthält die Spalte „married“ aus dem [DataTable]-Objekt, das mit dem [DataGrid] verknüpft wird. Diese erste Spalte des [DataGrid] wird im Assistenten wie folgt definiert:

Image

Kopfzeilentext
Spaltentitel, hier „Verheiratet“
Datenfeld
Name der Spalte in der Datenquelle, die von dieser Spalte des [DataGrid] angezeigt wird. Hier ist es die Spalte „married“ der [DataTable].

Die zweite Spalte ist wie folgt definiert:

Kopfzeilentext
Kinder
Datenfeld
children

Die dritte Spalte ist wie folgt definiert:

Überschrift
Jahresgehalt
Datenfeld
Gehalt
Formatierungsausdruck
{0:C} – Währungsanzeige, um das Euro-Zeichen zu erhalten

Die vierte Spalte ist wie folgt definiert:

Kopfzeilentext
Steuerbetrag
Datenfeld
Steuer
Formatierungsausdruck
{0:C} – Währungsanzeige, um das Euro-Zeichen zu erhalten

Wir speichern den Präsentationscode und den Steuerungscode in zwei separaten Dateien. Die erste befindet sich in [main.aspx] und die zweite in [main.aspx.vb]. Der Code für [main.aspx] lautet wie folgt:


<%@ 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. Der Steuerungscode der Anwendung

Der Anwendungssteuerungscode ist auf die Dateien [global.asax.vb] und [main.aspx.vb] verteilt. Die Datei [global.asax] ist wie folgt definiert:

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

Die Datei [global.asax.vb] sieht wie folgt aus:


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)
        ' create an impot object
        Dim objImpot As impot
        Try
            objImpot = New impot(New impotsOLEDB(ConfigurationSettings.AppSettings("chaineConnexion")))
            ' put the object in the application
            Application("objImpot") = objImpot
            ' no error
            Application("erreur") = False
        Catch ex As Exception
            'there has been an error, we note it in the application
            Application("erreur") = True
            Application("message") = ex.Message
        End Try
    End Sub
 
    Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs)
        ' start of session - create a list of empty simulations
        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

Beim Start der Anwendung (erste Anfrage an die Anwendung) wird die Prozedur [Application_Start] ausgeführt. Sie versucht, ein Objekt vom Typ [tax] zu erstellen, indem sie dessen Daten aus einer OLEDB-Quelle abruft. Der Leser wird gebeten, Kapitel 5 zu lesen, in dem diese Klasse definiert wurde, falls er dies vergessen hat. Die Erstellung des Objekts [impot] kann fehlschlagen, wenn die Datenquelle nicht verfügbar ist. In diesem Fall wird der Fehler in der Anwendung gespeichert, damit alle nachfolgenden Anfragen wissen, dass die Initialisierung nicht korrekt durchgeführt werden konnte. Ist die Erstellung erfolgreich, wird das erstellte [impot]-Objekt ebenfalls in der Anwendung gespeichert. Es wird von allen Anfragen zur Steuerberechnung verwendet. Wenn ein Client seine erste Anfrage stellt, wird für ihn durch die Prozedur [Application_Start] eine Sitzung erstellt. Diese Sitzung dient dazu, die verschiedenen Steuerberechnungssimulationen zu speichern, die der Client durchführen wird. Diese werden in einem [DataTable]-Objekt gespeichert, das mit dem Sitzungsschlüssel „simulations“ verknüpft ist. Beim Start der Sitzung wird dieser Schlüssel einem leeren [DataTable]-Objekt zugeordnet, dessen Struktur bereits definiert wurde. Die von der Anwendung benötigten Informationen befinden sich in ihrer Konfigurationsdatei [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>

Der Schlüssel [connectionString] gibt die Verbindungszeichenfolge zur OLEDB-Quelle an. Der restliche Steuerungscode befindet sich in [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
 
    ' local variables
 
    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

Das erste vom Code bearbeitete Ereignis ist [Page_Load]:


    Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
        ' first, we look at the state of the application
        If CType(Application("erreur"), Boolean) Then
            ' the application failed to initialize
            ' the error view is displayed
            Dim erreurs As New ArrayList
            erreurs.Add("Application momentanément indisponible (" + CType(Application("message"), String) + ")")
            afficheErreurs(erreurs)
            Exit Sub
        End If
        ' no errors - on the 1st request, the form is presented
        If Not IsPostBack Then afficheFormulaire()
    End Sub

Bevor wir die Anfrage bearbeiten, überprüfen wir, ob die Anwendung korrekt initialisiert wurde. Ist dies nicht der Fall, zeigen wir die Ansicht [errors] mithilfe der Prozedur [displayErrors] an. Handelt es sich um die erste Anfrage (IsPostBack=false), zeigen wir die Ansicht [form] mithilfe von [displayForm] an.

Die Prozedur, die die Ansicht [errors] anzeigt, lautet wie folgt:


    Private Sub afficheErreurs(ByRef erreurs As ArrayList)
        ' builds a list of errors
        With rptErreurs
            .DataSource = erreurs
            .DataBind()
        End With
        ' container display 
        panelerreurs.Visible = True
        panelform.Visible = False
        panelsimulations.Visible = False
        Exit Sub
    End Sub

Die Prozedur erhält als Parameter eine Liste von Fehlermeldungen in [errors] vom Typ [ArrayList]. Wir binden diese Datenquelle einfach an die Komponente [rptErrors], die für deren Anzeige zuständig ist.

Die Prozedur zur Anzeige der Ansicht [form] lautet wie folgt:


    Private Sub afficheFormulaire()
        ' displays the form
        panelform.Visible = True
        ' the other containers are hidden
        panelerreurs.Visible = False
        panelsimulations.Visible = False
    End Sub

Diese Prozedur macht lediglich den Container [panelform] sichtbar. Die Komponenten werden mit ihrem übermittelten oder vorherigen Wert (VIEWSTATE) angezeigt.

Wenn der Benutzer in der Ansicht [form] auf die Schaltfläche [Calculate] klickt, wird eine POST-Anfrage an [main.aspx] gesendet. Die Prozedur [Page_Load] wird ausgeführt, gefolgt von der Prozedur [btnCalculate_Click]:


    Private Sub btnCalculer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnCalculer.Click
        ' we check the validity of the data entered; if there are any errors, we report them
        If Not Page.IsValid Then
            afficheFormulaire()
            Exit Sub
        End If
        ' no errors - tax is calculated
        Dim impot As Long = CType(Application("objImpot"), impot).calculer( _
        rdOui.Checked, CType(txtEnfants.Text, Integer), CType(txtSalaire.Text, Long))
        ' the result is added to existing simulations
        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)
        ' put the simulations in the session
        Session.Item("simulations") = simulations
        ' the simulations page is displayed
        afficheSimulations(simulations, "Retour au formulaire")
    End Sub

Die Prozedur beginnt mit der Überprüfung der Gültigkeit der Seite. Denken Sie daran, dass bei Ausführung der Prozedur [btnCalculate_Click] die Validierungssteuerelemente ihre Arbeit bereits erledigt haben und das Attribut [IsValid] der Seite gesetzt wurde. Ist die Seite ungültig, wird die Ansicht [form] erneut angezeigt und die Prozedur beendet. Die Validierungskomponenten der Ansicht [form], deren Attribut [IsValid] auf [false] gesetzt ist, zeigen ihr Attribut [ErrorMessage] an. Ist die Seite gültig, wird der Steuerbetrag unter Verwendung des Objekts [tax] berechnet, das beim Start in der Anwendung gespeichert wurde. Diese neue Simulation wird der Liste der bereits durchgeführten und in der Sitzung gespeicherten Simulationen hinzugefügt.

Schließlich wird die Ansicht [simulations] durch die folgende Prozedur [displaySimulations] angezeigt:


    Private Sub afficheSimulations(ByRef simulations As DataTable, ByRef lien As String)
        ' we link the datagrid to the simulations source
        With dgSimulations
            .DataSource = simulations
            .DataBind()
        End With
        ' link
        lnkForm2.Text = lien
        ' the views
        panelsimulations.Visible = True
        panelerreurs.Visible = False
        panelform.Visible = False
    End Sub

Die Prozedur hat zwei Parameter:

  • eine Liste von Simulationen in [simulations] vom Typ [DataTable]
  • einen Linktext in [link]

Die Datenquelle [simulations] ist an die Komponente gebunden, die für ihre Anzeige zuständig ist. Der Linktext wird in die Eigenschaft [Text] des Objekts [LinkButton] in der Ansicht gesetzt.

Wenn der Benutzer in der Ansicht [form] auf die Schaltfläche [Delete] klickt, wird die Prozedur [btnDelete_click] ausgeführt (immer nach [Page_Load]):


    Private Sub btnEffacer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnEffacer.Click
        ' displays the empty form
        razForm()
        afficheFormulaire()
    End Sub
 
    Private Sub razForm()
        ' empty the form
        rdOui.Checked = False
        rdNon.Checked = True
        txtEnfants.Text = ""
        txtSalaire.Text = ""
    End Sub

Der obige Code ist so einfach, dass er keiner Kommentare bedarf. Wir müssen noch die Klicks auf die Links [Fehler] und [Simulationen] in der Ansicht behandeln:


    Private Sub lnkForm1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm1.Click
        ' displays the form
        afficheFormulaire()
    End Sub
 
    Private Sub lnkForm2_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkForm2.Click
        ' displays the form
        afficheFormulaire()
    End Sub

Beide Prozeduren zeigen lediglich die Ansicht [form] an. Wir wissen, dass die Felder in dieser Ansicht einen Wert erhalten, der entweder der für sie gesendete Wert oder ihr vorheriger Wert ist. Da in diesem Fall die POST-Anfrage des Clients keine Werte für die Formularfelder sendet, werden diese auf ihre vorherigen Werte zurückgesetzt. Das Formular wird daher mit den vom Benutzer eingegebenen Werten angezeigt.

8.9.4. Tests

Alle von der Anwendung benötigten Dateien befinden sich in einem Ordner namens <application-path>:
Der Ordner [bin] enthält die DLL mit den von der Anwendung benötigten Klassen [impot], [impotsData] und [impotsOLEDB]:

Falls gewünscht, kann der Leser in Kapitel 5 nachlesen, wie die oben erwähnte Datei [impot.dll] erstellt wird. Sobald dies erledigt ist, wird der Cassini-Server mit den Parametern (<application-path>,/impots6) gestartet. Anschließend rufen wir die URL [http://impots6/main.aspx] über einen Browser auf:

Image

Wenn Sie die ACCESS-Datei [impots.mdb] in [impots1.mdb] umbenennen, wird die folgende Seite angezeigt:

Image

8.9.5. Fazit

Wir haben eine MVC-Anwendung, die ausschließlich serverseitige Komponenten verwendet. Durch den Einsatz dieser Komponenten konnten wir die Präsentationsschicht der Anwendung vollständig von ihrer Steuerungsschicht trennen. Dies war bisher nicht möglich, da der Präsentationscode zuvor Variablen enthielt, die vom Steuerungscode berechnet wurden und deren Werte HTML-Code enthielten. Dies ist nun nicht mehr der Fall.