Skip to content

8. Componenti server ASP - 2

8.1. Introduzione

Continuiamo il nostro lavoro sull'interfaccia utente esplorando:

  • componenti di convalida dei dati
  • come associare i dati ai componenti server
  • componenti server HTML

8.2. componenti di validazione dei dati

8.2.1. Introduzione

Nelle applicazioni basate su moduli, è essenziale verificare la validità dei dati inseriti. Nell'esempio del calcolo delle imposte, avevamo il seguente modulo:

Image

Una volta ricevuti i valori da questo modulo, dobbiamo verificare la validità dei dati inseriti: il numero di figli e lo stipendio devono essere numeri interi positivi o zero. Se così non fosse, il modulo viene restituito al client così come è stato inserito, insieme ai messaggi di errore. Abbiamo gestito questo scenario utilizzando due viste, una per il modulo sopra riportato e l'altra per gli errori:

Image

ASP.NET offre componenti denominati componenti di convalida che consentono di verificare quanto segue:

Componente
Ruolo
RequiredFieldValidator
verifica che un campo non sia vuoto
CompareValidator
confronta due valori tra loro
RangeValidator
verifica che un valore rientri tra due limiti
Validatore di espressione regolare
verifica che un campo corrisponda a un'espressione regolare
CustomValidator
consente allo sviluppatore di definire le proprie regole di convalida: questo componente potrebbe sostituire tutti gli altri
ValidationSummary
consente di raccogliere i messaggi di errore generati dai controlli precedenti in un unico punto della pagina

Ora presenteremo ciascuno di questi componenti.

8.2.2. RequiredFieldValidator

Stiamo creando la seguente pagina [requiredfieldvalidator1.aspx]:

No.
nome
tipo
proprietà
ruolo
1
txtNome
Casella di testo
EnableViewState=true
campo di immissione
2
ValidatoreCampoObbligatorio1
ValidatoreCampoObbligatorio
EnableViewState=false
EnableClientScript=true
Messaggio di errore=Il campo [nome] è obbligatorio
componente di convalida
3
btnInvia
Pulsante
EnableViewState=false
ValidationReasons=true
Pulsante [submit]

Il campo [RequiredFieldValidator1] viene utilizzato per visualizzare un messaggio di errore se il campo [txtName] è vuoto o contiene una stringa di spazi. Le sue proprietà sono le seguenti:

ControlToValidate
Il campo il cui valore deve essere convalidato dal componente. Cosa significa il valore di un componente? È il valore dell'attributo [value] del tag HTML corrispondente. Per un [TextBox], si tratta del contenuto del campo di immissione; per un [DropDownList], è il valore dell'elemento selezionato.
EnableClientScript
Booleano - se vero, indica che anche il contenuto del campo precedente deve essere convalidato sul lato client. In questo caso, il modulo verrà inviato dal browser solo se non contiene errori. Tuttavia, i controlli di convalida vengono eseguiti anche sul server nel caso in cui il client non sia un browser, ad esempio.
Messaggio di errore
Il messaggio di errore che il componente deve visualizzare se viene rilevato un errore

La convalida dei dati sulla pagina viene eseguita solo se il pulsante o il collegamento che ha attivato il [POST] della pagina ha la proprietà [CausesValidation=true]. [true] è il valore predefinito per questa proprietà.

Eseguiamo questa applicazione. Per prima cosa, useremo un browser [Mozilla]:

Image

Il codice HTML ricevuto da [Mozilla] è il seguente:

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

Possiamo notare che il pulsante [btnEnvoyer] è collegato a una funzione JavaScript tramite il suo attributo [onclick]. Possiamo anche notare che la pagina non contiene codice [JavaScript]. Il test [typeof(Page_ClientValidate) == 'function'] fallirà e la funzione JavaScript non verrà chiamata. Il modulo verrà quindi inviato al server, che eseguirà i controlli di validazione. Utilizziamo il pulsante [Submit] senza inserire alcun nome. La risposta del server è la seguente:

Image

Cosa è successo? Il modulo è stato inviato al server. Il server ha eseguito il codice per tutti i componenti di convalida presenti nella pagina. In questo caso, ce n'è solo uno. Se almeno un componente di convalida rileva un errore, il server restituisce il modulo così come è stato compilato (infatti, per i componenti con [EnableViewState=true], include anche un messaggio di errore per ogni componente di convalida che ha rilevato un errore). Questo messaggio è l'attributo [ErrorMessage] del componente di convalida. Questo è il meccanismo che vediamo all'opera sopra. Se inseriamo qualcosa nel campo [name], il messaggio di errore non appare più quando il modulo viene inviato:

Image

Ora, utilizziamo Internet Explorer e richiediamo l'URL [http://localhost/requiredfieldvalidator1.aspx]. Otteniamo la seguente pagina:

Image

Il codice HTML ricevuto da IE è il seguente:

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

Possiamo notare che questo codice è molto più lungo di quello ricevuto da [Mozilla]. Non entreremo nei dettagli. La differenza deriva dal fatto che il server ha incluso funzioni JavaScript nel documento HTML inviato. Perché ci sono due codici HTML diversi quando l'URL richiesto da entrambi i browser è lo stesso? Abbiamo già osservato che la tecnologia ASP.NET fa sì che il server adatti il documento HTML inviato al client all'ambiente specifico di quel client. Sul mercato sono disponibili diversi browser e non tutti hanno le stesse funzionalità. I browser Microsoft e Netscape, ad esempio, non utilizzano lo stesso modello a oggetti per il documento che ricevono. Di conseguenza, il codice JavaScript utilizzato per manipolare questo documento sul lato client differisce tra i due browser. Allo stesso modo, i produttori di browser hanno rilasciato versioni successive dei loro browser, migliorandone continuamente le funzionalità. Pertanto, un documento HTML che IE5 è in grado di analizzare potrebbe non essere compreso da IE3. Quello sopra riportato è un esempio di questo adattamento lato server per il client. Per un motivo non approfondito in questa sede, il server web ha determinato che il client [Mozilla] non aveva la capacità di gestire il codice JavaScript di convalida. Pertanto, questo codice non è stato incluso nel documento HTML inviato e la convalida è stata eseguita sul lato server. Per [IE6], questo codice JavaScript è stato incluso nel documento HTML inviato, come possiamo vedere. Per vederlo in azione, proviamo il seguente esperimento:

  • fermare il server web
  • fare clic sul pulsante [Invia] senza compilare il campo [Nome]

Otteniamo la seguente nuova pagina:

Image

È effettivamente il browser che ha visualizzato il messaggio di errore. Questo perché il server è arrestato. Possiamo verificarlo inserendo qualcosa nel campo [name] e inviando il modulo. Questa volta, la risposta è la seguente:

Image

Cosa è successo? Il browser ha eseguito il codice di validazione JavaScript. Questo codice ha determinato che la pagina era valida. Il browser ha quindi inviato il modulo al server web, che era fermo. Il browser se ne è reso conto e ha visualizzato la pagina sopra riportata. Se riavviamo il server, tutto torna alla normalità.

Cosa possiamo imparare da questo esempio?

  • l'importanza del concetto di componente di convalida, che consente di rinviare al client qualsiasi modulo non corretto
  • il valore di [VIEWSTATE], che consente di restituire il modulo esattamente come è stato inserito
  • la capacità del server di adattarsi al proprio client. Il client viene identificato dall'intestazione HTTP [User-Agent:] che invia al server. Non è quindi il server a "indovinare" con chi ha a che fare. Questa adattabilità è di grande interesse per lo sviluppatore, che non deve preoccuparsi del tipo di client per la propria applicazione.

8.2.3. CompareValidator

Stiamo creando la seguente pagina [comparevalidator1.aspx]:

Image

No.
nome
tipo
proprietà
ruolo
1
cmbChoix1
Elenco a discesa
EnableViewState=true
elenco a discesa
2
cmbChoix2
Elenco a discesa
EnableViewState=true
elenco a discesa
3
CompareValidator1
CompareValidator
EnableViewState=false
AbilitaScriptClient=true
Messaggio di errore=Scelta non valida 1
ValoreDaConfrontare=?
Operatore=Non uguale
Tipo=stringa
componente di convalida
4
CompareValidator2
CompareValidator
EnableViewState=false
EnableClientScript=true
Messaggio di errore=Scelta non valida 2
ControlToCompare=?
Operatore=Non uguale
Tipo=stringa
componente di validazione
5
btnInvia
Pulsante
EnableViewState=false
Motivi di convalida=true
Pulsante [submit]

Il componente [CompareValidator] viene utilizzato per confrontare due valori. Gli operatori disponibili sono [Equal, NotEqual, LessThan, LessThanEqual, GreaterThan, GreaterThanEqual]. Il primo valore è impostato dalla proprietà [ControlToValidate], il secondo da [ValueToCompare] se il primo valore deve essere confrontato con una costante, oppure da [ControlToCompare] se deve essere confrontato con il valore di un altro componente. Le proprietà importanti del componente [CompareValidator] sono le seguenti:

ControlToValidate
campo il cui contenuto deve essere convalidato dal componente
EnableClientScript
Booleano - se vero, indica che il contenuto del campo precedente deve essere convalidato anche sul lato client
Messaggio di errore
il messaggio di errore che il componente deve visualizzare se viene rilevato un errore
ValueToCompare
il valore con cui deve essere confrontato il valore del campo [ControlToValidate]
ControlToCompare
il componente con cui deve essere confrontato il valore del campo [ControlToValidate]
Operatore
operatore di confronto tra i due valori
Tipo
Tipo dei valori da confrontare

Il codice HTML di questa pagina è il seguente:

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

I vincoli di convalida per la pagina sono i seguenti:

  • Il valore selezionato in [cmbChoix1] deve essere diverso dalla stringa "?". Ciò implica le seguenti proprietà per il componente [CompareValidator1]: Operator=NotEqual, ValueToCompare=?, Type=string
  • Il valore selezionato in [cmbChoix2] deve essere diverso dal valore selezionato in [cmbChoix1]. Ciò implica le seguenti proprietà per il componente [CompareValidator2]: Operator=NotEqual, ControlToCompare=cmbChoix1, Type=string

Eseguiamo questa applicazione. Ecco un esempio di una pagina ricevuta durante gli scambi client-server:

Image

Se la stessa pagina viene richiesta da Internet Explorer 6, in essa è incluso del codice JavaScript che determina un comportamento leggermente diverso. Eventuali errori vengono segnalati non appena l'utente modifica un valore in uno degli elenchi a discesa. Ciò migliora l'esperienza utente.

8.2.4. CustomValidator , RangeValidator

Stiamo creando la seguente pagina [customvalidator1.aspx]:

Image

No.
nome
tipo
proprietà
ruolo
1
cmbDiplomi
Elenco a discesa
EnableViewState=true
elenco a discesa
2
Validatore di confronto 2
CompareValidator
EnableViewState=false
AbilitaScriptClient=true
ErrorMessage=Diploma non valido
Operatore=Non uguale
ValueToCompare=?
Tipo=Stringa
componente di validazione del controllo [1]
3
txtOtherDegree
Casella di testo
EnableViewState=false
campo di immissione
4
Validatore personalizzato1
Validatore personalizzato
EnableViewState=false
EnableClientScript=true
ErrorMessage=Specifiche del titolo di studio non valide
ClientValidationFunction=chkOtherDegree
componente di convalida per il campo [3]
5
txtAnDiploma
Casella di testo
EnableViewState=false
campo di immissione
6
ValidatoreIntervallo1
RangeValidator
EnableViewState=false
EnableClientScript=true
ErrorMessage=L'anno di laurea deve rientrare nell'intervallo [1990,2004]
MinValue=1990
MaxValue=2004
Type=Integer
ControlToValidate=txtAnDiplome
componente di validazione campo [5]
7
ValidatoreCampoObbligatorio2
ValidatoreCampoObbligatorio
EnableViewState=false
AbilitaScriptClient=vero
ErrorMessage=È necessario specificare l'anno di laurea
ControlToValidate=txtAnDiplome
componente di convalida campo [5]
8
btnSubmit
Pulsante
EnableViewState=false
Motivi di convalida=true
Pulsante [submit]

Il campo [RangeValidator] viene utilizzato per verificare che il valore di un controllo rientri tra due limiti [MinValue] e [MaxValue]. Le sue proprietà sono le seguenti:

ControlToValidate
campo il cui valore deve essere convalidato dal componente
EnableClientScript
Booleano - se vero, indica che anche il contenuto del campo precedente deve essere convalidato sul lato client
ErrorMessage
il messaggio di errore che il componente deve visualizzare se viene rilevato un errore
MinValue
valore minimo del campo da convalidare
MaxValue
valore massimo del campo da verificare
Tipo
Tipo del valore del campo da convalidare

Il campo [CustomValidator] consente di eseguire convalide che non possono essere eseguite dai componenti di convalida forniti da ASP.NET. Questa convalida viene eseguita da una funzione scritta dallo sviluppatore. Questa funzione viene eseguita sul lato server. Poiché la convalida può essere eseguita anche sul lato client, lo sviluppatore potrebbe dover sviluppare una funzione JavaScript da includere nel documento HTML. Le sue proprietà sono le seguenti:

ControlToValidate
Il campo il cui valore deve essere convalidato dal componente
EnableClientScript
Booleano - se true, indica che il contenuto del campo precedente deve essere convalidato anche sul lato client
ErrorMessage
il messaggio di errore che il componente deve visualizzare se viene rilevato un errore
ClientValidationFunction
La funzione da eseguire sul lato client

Il codice del modello di pagina è il seguente:

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

L'attributo [OnServerValidate] del componente [CustomValidator] consente di specificare la funzione responsabile della convalida lato server. Nell'esempio sopra riportato, il componente [CustomValidator1] chiama la seguente procedura [CustomValidator1_ServerValidate_1]:

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

Una funzione di convalida associata a un componente [CustomValidator] riceve due parametri:

  • sender: l'oggetto che ha attivato l'evento
  • e: l'evento. La procedura deve impostare la proprietà [e.IsValid] su true se i dati convalidati sono corretti e su false in caso contrario.

Qui vengono eseguiti i seguenti controlli:

  • l'elenco a discesa [cmbDiplomes] non può avere [?] come valore. Ciò viene verificato dal componente [CompareValidator2]
  • l'utente seleziona un titolo di studio dall'elenco a discesa [cmbDiplomes]. Se il titolo di studio non è presente nell'elenco, è possibile selezionare l'opzione [Other] dall'elenco. È quindi necessario inserire il titolo di studio nel campo di immissione [txtAutreDiplome]. Se in [cmbDiplomes] è selezionato [Other], il campo [txtAutreDiplome] non deve essere vuoto. Se in [cmbDiplomes] non è selezionato né [Altro] né [?], il campo [txtAutreDiplome] deve essere vuoto. Ciò viene verificato dalla funzione associata al componente [CustomValidator1].
  • L'anno di conseguimento del titolo di studio deve rientrare nell'intervallo [1900-2004]. Ciò viene verificato da [RangeValidator1]. Tuttavia, se l'utente lascia il campo vuoto, la funzione di convalida di [RangeValidator1] non viene utilizzata. Pertanto, aggiungiamo il componente [RequiredFieldValidator2] a [ ] per verificare la presenza di contenuto. Questa è una regola generale. Il contenuto di un campo non viene verificato se è vuoto. L'unico caso in cui viene verificato è quando è associato a un componente [RequiredFieldValidator].

Ecco un esempio di esecuzione nel browser [Mozilla]:

Image

Se utilizziamo il browser [IE6], possiamo aggiungere una funzione di convalida lato client per il componente [CustomValidator1]. A tal fine, questo componente presenta le seguenti proprietà:

  • EnableClientScript=true
  • ClientValidationFunction=chkAutreDiplome

La funzione [chkAutreDiplome] è la seguente:

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

La funzione [chkAutreDiplome] riceve gli stessi due parametri della funzione server [CustomValidator1_ServerValidate_1]:

  • source: l'oggetto che ha attivato l'evento
  • args: l'evento. Questo ha un attributo [IsValid] che deve essere impostato su true dalla funzione se i dati convalidati sono corretti. Un valore false visualizzerà il messaggio di errore associato nella posizione del componente di convalida e il modulo non verrà inviato al server.

8.2.5. RegularExpressionValidator

Stiamo creando la seguente pagina [regularexpressionvalidator1.aspx]:

Image

No.
nome
tipo
proprietà
ruolo
1
txtMel
Casella di testo
EnableViewState=true
campo di immissione
2
ValidatoreCampoObbligatorio3
ValidatoreCampoObbligatorio
ControlToValidate=txtMel
Display=Dynamic
componente di convalida controllo [1]
3
Validatore di espressioni regolari 1
Validatore di espressioni regolari
ControlToValidate=txtMel
Visualizza=Dinamico
componente di convalida controllo [1]
4
btnInvia
Pulsante
EnableViewState=false
CausesValidation=true
Pulsante [submit]

In questo caso, vogliamo convalidare il formato di un indirizzo e-mail. Lo facciamo utilizzando un componente [RegularExpressionValidator], che ci permette di convalidare un campo utilizzando un'espressione regolare. Ricordiamo che un'espressione regolare è un modello di caratteri. Pertanto, verifichiamo il contenuto di un campo rispetto a un modello. Poiché il contenuto di un campo non viene controllato se è vuoto, abbiamo anche bisogno di un componente [RequiredFieldValidator] per verificare che l'indirizzo e-mail non sia vuoto. La classe [RegularExpressionValidator] ha proprietà simili a quelle delle classi dei componenti già trattate:

ControlToValidate
campo il cui valore deve essere convalidato dal componente
EnableClientScript
booleano - se vero, indica che anche il contenuto del campo precedente deve essere convalidato sul lato client
ErrorMessage
il messaggio di errore che il componente deve visualizzare se viene rilevato un errore
EspressioneRegolare
l'espressione regolare con cui verrà confrontato il contenuto di [ControlToValidate]
Visualizza
modalità di visualizzazione: Static: il campo è sempre presente anche se non visualizza un messaggio di errore; Dynamic: il campo è presente solo se c'è un messaggio di errore; None: il messaggio di errore non viene visualizzato. Questo campo esiste anche per altri componenti ma non era stato utilizzato fino ad ora.

Il modello di espressione regolare per un indirizzo e-mail potrebbe essere il seguente: \s*[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+\s*

Un indirizzo e-mail ha la forma [champ1.champ2....@champA.champB...]. Devono esserci almeno un campo prima del segno @ e almeno due campi dopo di esso. Questi campi sono costituiti da caratteri alfanumerici e dal segno -. Un carattere alfanumerico è rappresentato dal simbolo \w. La sequenza [\w-] indica il carattere \w o il carattere -. Il segno + dopo una sequenza S significa che può essere ripetuta 1 o più volte; il segno * significa che può essere ripetuta 0 o più volte; il segno ? significa che può essere ripetuta 0 o 1 volta.

Un campo nell'indirizzo e-mail corrisponde al modello [\w-]+. Il fatto che debba esserci almeno un campo prima del segno @ e almeno due dopo di esso, separati dal segno ., corrisponde al modello: [\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+. Possiamo consentire all'utente di includere spazi prima e dopo l'indirizzo. Questi verranno rimossi durante l'elaborazione. Una sequenza di spazi, che può essere vuota, è rappresentata dal modello \s*. Da qui l'espressione regolare per l'indirizzo e-mail: \s*[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+\s*.

Il codice di impaginazione risulta quindi il seguente:

<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. Riepilogo di convalida

Per motivi estetici, potresti voler raggruppare i messaggi di errore in un'unica posizione, come nell'esempio seguente [summaryvalidator1.aspx], dove abbiamo combinato tutti gli esempi precedenti in un'unica pagina:

Image

Tutti i controlli di convalida hanno le seguenti proprietà:

  • [Display=Dynamic], che garantisce che i controlli non occupino spazio sulla pagina se il loro messaggio di errore è vuoto
  • [EnableClientScript=false] per disabilitare tutte le convalide lato client
  • [Text=*]. Questo sarà il messaggio visualizzato in caso di errore, mentre il contenuto dell'attributo [ErrorMessage] viene visualizzato dal controllo [ValidationSummary] mostrato di seguito.

Questo insieme di controlli è inserito in un componente [Panel] denominato [vueFormulaire]. In un altro componente [Panel] denominato [vueErreurs], inseriamo un controllo [ValidationSummary]:

Image

No.
nome
tipo
proprietà
ruolo
1
Riepilogo di convalida 1
RiepilogoConvalida
HeaderText=Si sono verificati i seguenti errori, EnableClientScript=false, ShowSummary=true
visualizza gli errori provenienti da tutti i controlli di convalida presenti nella pagina
2
ErroriCollegatiAlModulo
Pulsante di collegamento
ValidationCauses=false
collegamento al modulo

Per il collegamento [lnkErrorsToForm], non è necessario abilitare la convalida poiché questo collegamento non invia alcun valore da verificare.

Quando tutti i dati sono validi, all'utente viene visualizzata la seguente vista [info]:

Image

1

No.
Nome
tipo
proprietà
ruolo
1
lblInfo
Etichetta
 
Messaggio informativo per l'utente

Il codice HTML di questa pagina è il seguente:

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

Ecco alcuni esempi dei risultati ottenuti. Inviamo la vista [form] senza inserire alcun valore:

Image

Il pulsante [Invia] ci fornisce la seguente risposta:

Image

Viene visualizzata la vista [errors]. Se utilizziamo il link per tornare al modulo, lo troviamo nel seguente stato:

Image

Questa è la vista [form]. Notate il carattere [*] accanto ai dati errati. Questo è il campo [Text] dei controlli di validazione che è stato visualizzato. Se compiliamo i campi correttamente, otterremo la vista [info]:

Image

Il codice di controllo della pagina è il seguente:

<%@ 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>
  • Nella procedura [Page_Load], che viene eseguita ad ogni richiesta del client, visualizziamo la vista [form], mentre le altre vengono nascoste. Questo viene fatto solo alla prima richiesta. La procedura chiama una procedura di utilità [displayViews], alla quale passiamo tre valori booleani per determinare se visualizzare o meno le tre viste.
  • Quando viene chiamata la procedura [btnEnvoyer_Click], la convalida dei dati è già stata eseguita. Il pulsante [btnEnvoyer] ha la proprietà [CausesValidation=true], che attiva questa convalida dei dati. Tutti i controlli di convalida sono stati eseguiti e la loro proprietà [IsValid] è stata impostata. Questa proprietà indica se i dati convalidati dal controllo erano validi o meno. Inoltre, è stata impostata anche la proprietà [IsValid] della pagina stessa. È [true] solo se la proprietà [IsValid] di tutti i controlli di convalida presenti nella pagina ha il valore [true]. Pertanto, la procedura [btnEnvoyer_Click] inizia verificando se la pagina è valida o meno. Se non è valida, viene visualizzata la vista [errors]. Questa vista contiene il controllo [ValidationSummary], che elenca gli attributi [ErrorMessage] di tutti i controlli (vedi screenshot sopra). Se la pagina è valida, viene visualizzata la vista [info] insieme a un messaggio informativo.
  • La procedura [lnkErrorsToForm_Click] è responsabile della visualizzazione della vista [form] nello stato in cui è stata convalidata. Poiché tutti i campi di immissione nella vista [form] hanno la proprietà [EnableViewState=true], il loro stato viene rigenerato automaticamente. Curiosamente, lo stato dei componenti di convalida non viene ripristinato. Ci si potrebbe aspettare, al ritorno alla vista [errors], di vedere i controlli non validi visualizzare il loro campo [Text]. Non è così. Abbiamo quindi forzato la convalida dei dati utilizzando il metodo [Page.Validate] della pagina. Ciò deve essere fatto una volta che il pannello [formView] è stato reso visibile. Si hanno quindi due convalide in totale. Questo dovrebbe essere evitato nella pratica. In questo caso, l’esempio ci ha permesso di introdurre nuovi concetti relativi alla convalida della pagina.

8.3. Componenti ListControl e associazione dati

Molti dei componenti server trattati consentono di visualizzare un elenco di valori (DropDownList, ListBox). Altri che non abbiamo ancora trattato consentono di visualizzare più elenchi di valori in tabelle HTML. Per tutti questi componenti, è possibile associare programmaticamente i valori presenti negli elenchi al componente corrispondente uno per uno. È inoltre possibile associare a questi componenti oggetti più complessi, come oggetti di tipo [Array], [ArrayList], [DataSet], [HashTable], ecc., il che semplifica il codice che associa i dati al componente. Questa associazione è chiamata binding dei dati.

Tutti i componenti derivati dalla classe [ListControl] possono essere associati a un elenco di dati. Questi includono i componenti [DropDownList], [ListBox], [CheckButtonList] e [RadioButtonList]. Ciascuno di questi componenti può essere associato a un'origine dati. Ciò può assumere varie forme: [Array], [ArrayList], [DataTable], [DataSet], [HashTable], ... in generale un oggetto che implementa una delle interfacce IEnumerable, ICollection o IListSource. Ne presenteremo solo alcune qui. Un oggetto [DataSet] è una rappresentazione di un database relazionale. Si tratta quindi di un insieme di tabelle collegate da relazioni. L'oggetto [DataTable] rappresenta una di queste tabelle. L'origine dati imposta le proprietà [Text] e [Value] di ciascun [Item] nell'oggetto [ListControl]. Se T è il valore di [Text] e V è il valore di [Value], il tag HTML generato per ciascun elemento di [ListControl] è il seguente:

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

Un componente [ListControl] è associato a un'origine dati tramite le seguenti proprietà:

DataSource
una fonte dati [Array], [ArrayList], [DataTable], [DataSet], [HashTable], ...
DataMember
se l'origine dati è un [DataSet], rappresenta il nome della tabella da utilizzare come origine dati. L'origine dati effettiva è quindi una tabella.
DataTextField
se l'origine dati è una tabella ([DataTable], [DataSet]), rappresenta il nome della colonna della tabella che fornirà i valori al campo [Text] degli elementi [ListControl]
DataValueField
se l'origine dati è una tabella ([DataTable], [DataSet]), rappresenta il nome della colonna della tabella che fornirà i valori al campo [Value] degli elementi [ListControl]

Il binding di un componente [ListControl] a una fonte dati non inizializza il componente con i valori della fonte dati. A farlo è l'operazione [ListControl].DataBind.

A seconda della natura dell'origine dati, il suo collegamento a un componente [ListControl] avverrà in modo diverso:

Array A
[ListControl].DataSource=A
I campi [Text] e [Value] degli elementi [ListControl] avranno i valori degli elementi in A
ArrayList AL
[ListControl].DataSource=AL
I campi [Text] e [Value] degli elementi [ListControl] avranno i valori degli elementi in AL
DataTable DT
[ListControl].DataSource = DT, [ListControl].DataTextField = "col1", [ListControl].DataValueField = "col2"
dove col1 e col2 sono due colonne della tabella DT. I campi [Text] e [Value] delle voci [ListControl] conterranno i valori delle colonne col1 e col2 della tabella DT
DataSet DS
[ListControl].DataSource=DS, [ListControl].DataSource="table" dove "table" è il nome di una delle tabelle in DS.
[ListControl].DataTextField="col1", [ListControl].DatavalueField ="col2" dove col1 e col2 sono due colonne della tabella "table". I campi [Text] e [Value] degli elementi [ListControl] avranno i valori delle colonne col1 e col2 della tabella "table"
Tabella hash HT
[ListControl].DataSource = HT, [ListControl].DataTextField = "key", [ListControl].DataValueField = "value", dove [key] e [value] sono rispettivamente le chiavi e i valori di HT.

Applichiamo queste informazioni al seguente esempio [databind1.aspx]:

Qui abbiamo cinque associazioni dati, ciascuna con quattro controlli dei tipi [DropDownList], [ListBox], [CheckBoxList] e [RadioButtonList]. Le cinque associazioni esaminate differiscono per le loro origini dati:

collegamento
origine dati
1
Array
2
ArrayList
3
DataTable
4
DataSet
5
Tabella hash

8.3.1. Codice di presentazione dei componenti

Il codice di presentazione per i controlli nel binding 1 è il seguente:

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

Il binding per i binding da 2 a 5 è identico, tranne che per il numero di binding.

8.3.2. Associazione a un'origine dati di tipo array

Il binding dei quattro controlli [ListBox] sopra riportati viene effettuato come segue nella procedura [Page_Load] del codice del controllo:

' 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

I controlli vengono associati ai dati qui solo alla prima richiesta. Successivamente, i controlli manterranno i propri elementi tramite il meccanismo [VIEWSTATE]. L'associazione viene eseguita nella procedura [bindToArray]. Poiché l'origine dati è di tipo [Array], viene inizializzato solo il campo [DataSource] dei componenti [ListControl]. Il controllo viene popolato con i valori provenienti dall'origine dati associata utilizzando il metodo [ListControl].DataBind. Solo a questo punto gli oggetti [ListControl] dispongono di elementi. È quindi possibile selezionarne alcuni.

8.3.3. Associazione a un'origine dati ArrayList

L'origine dati [myDataList] viene inizializzata nella procedura [createDataSources]:

' 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

Il binding dei quattro controlli [ListBox] nel binding 2 viene effettuato come segue nella procedura [bindToArrayList] del codice di controllo:

' 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

Poiché l'origine dati è di tipo [ArrayList], viene inizializzato solo il campo [DataSource] dei componenti [ListControl].

8.3.4. Origine dati di tipo DataTable

L'origine dati [myDataTable] viene inizializzata nella procedura [createDataSources]:

' 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

Iniziamo creando un oggetto [DataTable] con due colonne: [id] e [text]. La colonna [id] popolerà il campo [Value] degli elementi [ListControl], mentre la colonna [text] popolerà i loro campi [Text]. Il [DataTable] con due colonne viene creato come segue:

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

Creiamo così una tabella con due colonne:

  • la prima, denominata "id", è di tipo intero
  • la seconda, denominata "text", è di tipo stringa

Ora che la struttura della tabella è stata creata, possiamo popolarla con il seguente codice:

  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

La colonna [id] conterrà numeri interi [0,1,..,n], mentre la colonna [text] conterrà i valori dell'array [data]. Una volta fatto questo, la tabella [dataList] viene popolata. Il binding dei quattro controlli [ListBox] nel binding 3 viene eseguito come segue nella procedura [bindToDataTable] del codice di controllo:

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

Ogni componente [ListControl] è associato all'origine dati [myDataTable] impostando quanto segue per ciascuno di essi:

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

La tabella [myDataTable] è l'origine dati. La colonna [id] di questa tabella popolerà i campi [Value] degli elementi del componente, mentre la colonna [text] popolerà i loro campi [Text].

8.3.5. Origine dati di tipo DataSet

L'origine dati [myDataSet] viene inizializzata nella procedura [createDataSources]:

' 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

Un oggetto [DataSet] rappresenta una raccolta di tabelle [DataTable]. Aggiungiamo la [myDataTable] creata in precedenza al [DataSet]. Il binding dei quattro controlli [ListBox] nel binding 4 viene effettuato come segue nella procedura [bindToDataSet] del codice di controllo:

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

Ogni componente [ListControl] è collegato all'origine dati come segue:

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

Il set di dati [myDataSet] è l'origine dati. Poiché può contenere più tabelle, specifichiamo il nome della tabella da utilizzare in [DataMember]. La colonna [id] di questa tabella popolerà i campi [Value] degli elementi del componente, mentre la colonna [text] popolerà i loro campi [Text].

8.3.6. Origine dati HashTable

L'origine dati [myHashTable] viene inizializzata nella procedura [createDataSources]:

' 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

Il dizionario [myHashTable] può essere visto come una tabella con due colonne chiamate "key" e "value". La colonna [key] rappresenta le chiavi del dizionario, mentre la colonna [value] rappresenta i valori ad esse associati. In questo caso, la colonna [key] è costituita dal contenuto dell'array [values], mentre la colonna [value] è costituita dal contenuto dell'array [texts]. Il binding di questa sorgente ai controlli viene eseguito nella procedura [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

Per ogni componente, il binding viene stabilito utilizzando le seguenti istruzioni:

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

L'origine dati è il dizionario [myHashTable]. I valori dei controlli sono forniti dalla colonna [key] del dizionario, mentre il testo dalla colonna [value]. Gli elementi del dizionario vengono inseriti nei controlli nell'ordine delle chiavi, che inizialmente è casuale.

8.3.7. Direttive di importazione dello spazio dei nomi

Diversi spazi dei nomi vengono importati automaticamente in una pagina ASP.NET. Questo non vale per "System.Data", dove si trovano le classi [DataTable] e [DataSet]. Pertanto, questa classe deve essere importata. Ciò avviene come segue:

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

8.4. Componente DataGrid e associazione dei dati

Il componente [DataGrid] consente di visualizzare i dati in formato tabella, ma va ben oltre la semplice visualizzazione:

  • offre la possibilità di configurare con precisione la "rappresentazione visiva" della tabella
  • permette di aggiornare l'origine dati

Il componente [DataGrid] è potente e complesso. Lo presenteremo passo dopo passo.

8.4.1. Visualizzazione di un Array, ArrayList, DataTable o DataSet come origine dati

Il componente [DataGrid] consente di visualizzare origini dati di tipo [Array], [ArrayList], [DataTable] e [DataSet] in una tabella HTML. Per questi quattro tipi di dati, è sufficiente associare l'origine alla proprietà [DataSource] del componente [DataGrid]:

DataSource
una fonte dati [Array], [ArrayList], [DataTable], [DataSet], ...
DataMember
se l'origine dati è un [DataSet], rappresenta il nome della tabella da utilizzare come origine dati. L'origine dati effettiva è quindi una tabella. Se questo campo viene lasciato vuoto, vengono visualizzate tutte le tabelle nel [DataSet].

Presentiamo ora la pagina [datagrid1.aspx], che mostra un [DataGrid] associato a quattro diverse origini dati:

Image

La pagina contiene quattro componenti [DataGrid] creati con [WebMatrix] come segue. Trasciniamo il componente nella sua posizione nella scheda [Design]:

Image

Viene quindi disegnata una tabella HTML generica. Le proprietà di un [DataGrid] possono essere definite in fase di progettazione. È ciò che stiamo facendo qui per le sue proprietà di formattazione. Per farlo, selezioniamo il [DataGrid] da configurare. Le sue proprietà appaiono in una finestra in basso a destra:

Image

Utilizzeremo i due collegamenti sopra indicati. Il collegamento [Property Generator] consente di accedere alle proprietà principali del [DataGrid]:

Image

Deselezioniamo l'opzione [Show Header] per i quattro componenti [DataGrid] e salviamo la pagina. L'altro link, [Auto Format], consente di scegliere tra diversi stili per la tabella HTML che verrà visualizzata:

Image

Selezioniamo [color i] per [DataGrid] #i. Queste scelte di progettazione si riflettono nel codice di presentazione della pagina:

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

In modalità progettazione, abbiamo impostato solo le proprietà di formattazione. È nel codice di controllo che associamo i dati ai quattro componenti:

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

Il codice è molto simile a quello dell'esempio precedente, quindi non lo commenteremo in modo specifico. Si noti, tuttavia, come viene eseguito il binding dei dati. Per ciascuno dei quattro controlli, è sufficiente la seguente sequenza:

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

dove [data source] è di tipo [Array], [ArrayList], [DataTable] o [DataSet]. Possiamo quindi vedere che si tratta di uno strumento potente per visualizzare i dati in tabelle:

  • Il layout viene creato utilizzando [WebMatrix] o qualsiasi altro IDE durante la fase di progettazione
  • Il binding dei dati viene effettuato nel codice. Con [WebMatrix], è possibile farlo in fase di progettazione se l'origine dati è un database SQL Server o un database Access. In questo caso, un componente [SqlDataSource] o [AccessDataSource] viene posizionato sul modulo. Questo componente può essere collegato in fase di progettazione all'origine dati fisica, che sia un database SQL Server o un database Access, a seconda dei casi. Se si assegna un oggetto [SqlDataSource] o [AccessDataSource] collegato a una fonte fisica alla proprietà [DataSource] di un componente [DataGrid], i dati effettivi appariranno nel componente [DataGrid] in modalità di progettazione.

8.5. ViewState dei componenti dell'elenco dati

In questa sezione intendiamo mettere in evidenza il meccanismo [VIEWSTATE] per i componenti dell'elenco dati. È possibile scegliere tra due metodi per mantenere lo stato di un componente dell'elenco dati tra due richieste del client:

  • impostare il suo attributo [VIEWSTATE] su true
  • impostare il suo attributo [VIEWSTATE] su false e memorizzare la sua origine dati nella sessione in modo che il componente possa essere collegato a tale origine durante la richiesta successiva.

L'esempio seguente illustra alcuni aspetti del meccanismo [VIEWSTATE] per i contenitori di dati. Alla prima richiesta del client, l'applicazione visualizza la seguente vista:

No.
nome
tipo
proprietà
ruolo
1
DataGrid1
DataGrid
EnableViewState=true
visualizza un'origine dati S
2
DataGrid2
DataGrid
EnableViewState=true
visualizza la stessa origine dati S di [DataGrid1]
3
Pulsante1
Pulsante
EnableViewState=false
Pulsante [submit]

Il componente [DataGrid1] è gestito dal meccanismo [VIEWSTATE]. Vogliamo determinare se questo meccanismo, che rigenera la visualizzazione di [DataGrid1] ad ogni richiesta, rigenera anche la sua origine dati. Per farlo, l'origine dati è collegata al componente [DataGrid2]. La sua generazione ad ogni richiesta viene eseguita tramite un binding esplicito all'origine dati di [DataGrid1]. Anche il suo attributo [EnableViewState] è impostato su [true].

Il codice di presentazione [main.aspx] dell'applicazione è il seguente:


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

Il controller [main.aspx.vb] è il seguente:


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

Il metodo [createDataSource] crea un'origine dati S di tipo [DataTable]. Non ci soffermeremo sul suo codice, poiché non è l'oggetto principale di questo esempio. Questo è il metodo utilizzato per creare i due componenti [DataGrid] che ci interessano:

  • il componente [DataGrid1] viene associato alla tabella S una sola volta, durante la prima query. Da quel momento in poi non è più associato.
  • Il componente [DataGrid2] viene associato alla fonte [DataGrid1.DataSource] ad ogni nuova query.

Durante la prima query, otteniamo la seguente visualizzazione:

Image

Logicamente, entrambi i componenti visualizzano l'origine dati a cui erano collegati. Utilizziamo il pulsante [Submit] per attivare un [PostBack] al server. La vista risultante è quindi la seguente:

Image

Notiamo che il componente [DataGrid1] ha mantenuto il suo valore, mentre il componente [DataGrid2] no. Spiegazione:

  • Ancor prima che la procedura [Page_Load] abbia inizio, gli oggetti [DataGrid1] e [DataGrid2] hanno recuperato i valori che avevano durante la richiesta precedente, grazie al meccanismo [viewstate]. Infatti, entrambi hanno la proprietà [EnableViewState] impostata su [true].
  • Viene eseguita la procedura [Page_Load]. Poiché si tratta di un'operazione [PostBack], il componente [DataGrid1] non viene modificato da [Page_Load] (vedere il codice). Pertanto, mantiene il valore recuperato tramite [viewstate]. Questo è ciò che mostra la schermata sopra.
  • Il componente [DataGrid2], invece, è associato tramite [DataBind] alla fonte dati [DataGrid1.DataSource]. Viene quindi ricostruito e il valore che aveva appena recuperato tramite [viewstate] va perso. In questo caso sarebbe stato utile impostare la proprietà [EnableViewState] su [false] per evitare una gestione dello stato non necessaria. La schermata sopra mostra che [DataGrid2] è stato associato a una fonte vuota. Poiché questa fonte è [DataGrid1.DataSource], possiamo concludere che, mentre il meccanismo [viewstate] ripristina con successo la visualizzazione del componente [DataGrid1], non ripristina le sue proprietà come [DataSource].

Cosa possiamo concludere da questo esempio? È consigliabile evitare di impostare la proprietà [EnableViewState] di un contenitore di dati su [true] se deve essere associato (DataBind) a una fonte di dati ad ogni richiesta. Tuttavia, esistono casi specifici in cui, anche in questo scenario, la proprietà [EnableViewState] del contenitore deve rimanere impostata su [true]; in caso contrario, gli eventi che si desidera gestire non verranno attivati. Ne vedremo un esempio più avanti.

Spesso, l'origine dati di un contenitore dati cambia nel corso delle richieste. Pertanto, il contenitore deve essere associato all'origine dati ad ogni richiesta. È comune che l'origine dati sia limitata alla sessione in modo che le richieste possano accedervi. Il nostro secondo esempio illustra questo meccanismo. L'applicazione fornisce una sola vista:

No.
nome
tipo
proprietà
ruolo
1
DataGrid1
DataGrid
EnableViewState=true
visualizza un'origine dati S
2
DataGrid2
DataGrid
EnableViewState=false
visualizza la stessa origine dati S di [DataGrid1]
3
Pulsante1
Pulsante
EnableViewState=false
Pulsante [submit]
4
lblInfo1
lblInfo2
lblInfo3
Etichetta
EnableViewState=false
testo informativo

Il componente [DataGrid1] viene associato ai dati solo durante la prima richiesta. Manterrà il proprio valore tra una richiesta e l'altra grazie al meccanismo [viewstate]. Il componente [DataGrid2] viene associato a un'origine dati ad ogni richiesta, alla quale viene aggiunto un elemento ad ogni nuova richiesta. Pertanto, il componente [DataGrid2] deve essere associato (DataBind) ad ogni richiesta. Abbiamo quindi impostato il suo attributo [EnableViewState] su [false] come raccomandato in precedenza. Pertanto, durante la seconda richiesta (utilizzando il pulsante [Submit]), otteniamo la seguente risposta:

Image

Il componente [DataGrid1] ha mantenuto il suo valore iniziale. Il componente [DataGrid2] ha un elemento in più. I tre valori [1,2,2] rappresentano il numero della query. Possiamo vedere che uno dei valori è errato. Cercheremo di capire perché.

Il codice di presentazione dell'applicazione [main.aspx] è il seguente:


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

Il codice del controller [main.aspx.vb] è il seguente:


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

Non abbiamo riprodotto il codice per il metodo [createDataSource]. È lo stesso dell'applicazione precedente, tranne per il fatto che nel codice sorgente sono incluse solo tre righe. Vediamo innanzitutto come vengono gestiti l'origine dati e i due contenitori:


    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

Il componente [DataGrid1] viene associato all'origine dati S solo durante la prima query (non IsPostBack). Viene quindi inserito nella sessione. In seguito non verrà più inserito. L'origine dati S inserita nella sessione viene recuperata ad ogni richiesta e vi viene aggiunta una nuova riga. Il componente [DataGrid2] è esplicitamente associato alla fonte S ad ogni richiesta. Questo è il motivo per cui il suo contenuto aumenta di una riga ad ogni richiesta. Si noti che dopo aver modificato il contenuto della fonte S, questa non viene esplicitamente reinserita nella sessione tramite un'operazione:

            Session("source") = S

Perché? Quando viene avviata una query, la sessione contiene un oggetto Session("source") di tipo [DataTable], che è l'origine dati così com'era durante l'ultima query. Chiamiamo l'oggetto Session("source") S. Quando scriviamo:


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

[dtThèmes] e [S] sono due riferimenti allo stesso oggetto [DataTable]. Pertanto, quando il codice in [Page_Load] aggiunge un elemento alla tabella a cui fa riferimento [dtThèmes], lo aggiunge contemporaneamente alla tabella a cui fa riferimento [S]. Al termine dell'esecuzione della pagina, tutti gli oggetti nella sessione verranno salvati, compreso l'oggetto Session("source"), ovvero S, ovvero [dtThemes]. Pertanto, è effettivamente il nuovo contenuto dell'origine dati che viene salvato. Non era necessario scrivere:

            Session("source") = dtThèmes

per eseguire questo salvataggio, poiché Session("source") è già uguale a [dtThèmes]. Ciò non è più vero quando i dati inseriti nella sessione non sono oggetti, come le strutture [Integer, Float, ...]. Ciò è dimostrato dalla gestione del contatore delle query:


    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

Memorizziamo i contatori delle richieste in tre elementi:

  • numRequest1 e numRequest2 di tipo [Integer] - [Integer] non è una classe ma una struttura
  • numRequest3 di tipo [integer] - [integer] è una classe definita a questo scopo

Quando scriviamo:


        numRequête1 = CType(Session("numRequête1"), Integer)
..
        numRequête2 = CType(Session("numRequête2"), Integer)
..
        numRequête3 = CType(Session("numRequête3"), entier)
..
  • La struttura [Session("numRequest1")] viene copiata in [numRequest1]. Pertanto, quando l'elemento [numRequest1] viene modificato, l'elemento [Session("numRequest1")] stesso non lo è
  • Lo stesso vale per [Session("numRequest2")] e [numRequest2]
  • Gli elementi [Session("numRequête3")] e [numRequête3] sono entrambi riferimenti allo stesso oggetto di tipo [integer]. L'oggetto a cui si fa riferimento può essere modificato tramite entrambi i riferimenti.

Da ciò possiamo concludere che non è necessario scrivere:

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

per memorizzare il nuovo valore di [numRequest3] nella sessione. Invece, dovresti scrivere:

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

per memorizzare i nuovi valori delle strutture [numRequête1] e [numRequête2]. Lo facciamo solo per [numRequête2], il che spiega perché, nella schermata ottenuta dopo la seconda query, il contatore [numRequête1] non è corretto.

Dobbiamo quindi notare che una volta che i dati sono stati aggiunti a una sessione, non è necessario aggiungerli ripetutamente se sono rappresentati da un oggetto. In altri casi, devono essere aggiunti se sono stati modificati.

8.6. Visualizzazione di un elenco di dati utilizzando un DataGrid impaginato e ordinato

Il componente [DataGrid] consente di visualizzare il contenuto di un [DataSet]. Finora abbiamo sempre creato i nostri [DataSet] "a mano". Questa volta utilizzeremo un [DataSet] proveniente da un database. Stiamo realizzando la seguente applicazione MVC:

Le tre viste saranno incorporate nel codice di presentazione del controller [main.aspx] come contenitori. Pertanto, questa applicazione ha una singola pagina, [main.aspx].

8.6.1. Le classi di business

La classe [products] fornisce l'accesso al seguente database ACCESS:

Image

Il codice per la classe [products] è il seguente:


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

Il database ACCESS verrà gestito tramite un driver OLEDB. Forniamo al costruttore della classe [products] la stringa di connessione, il driver OLEDB e il database da gestire. La classe [products] dispone di un metodo [getDataSet] che restituisce un [DataSet] ottenuto eseguendo una query SQL [select] il cui testo viene passato come parametro. Durante l'esecuzione del metodo, avvengono diverse operazioni: stabilimento della connessione al database, esecuzione della query [select] e chiusura della connessione. Tutto ciò può generare un'eccezione, che viene gestita in questa sede. Un programma di prova potrebbe apparire così:

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

I vari componenti di questa applicazione vengono compilati come segue:

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. Le viste

Ora che abbiamo la classe di accesso ai dati, scriveremo i controller e le viste per questa applicazione web. Vediamo prima come funziona. La prima vista è la seguente:

N.
nome
tipo
proprietà
ruolo
1
txtSelect
Casella di testo
EnableViewState=true
campo di immissione query di selezione
2
ValidatoreCampoObbligatorio1
ValidatoreCampoObbligatorio
EnableViewState=false
verifica la presenza di 1
3
txtPages
TextBox
EnableViewState=true
campo di immissione - specifica il numero di righe di dati da visualizzare per pagina dei risultati
4
ValidatoreCampoObbligatorio2
ValidatoreCampoObbligatorio
EnableViewState=false
verifica la presenza di 3
5
RangeValidator1
RangeValidator
EnableViewState=false
verifica che (3) sia compreso nell'intervallo [1,30]
6
btnExecute
Pulsante
EnableViewState=false
Pulsante [submit]

Chiameremo questa vista [form]. Consente all'utente di eseguire una query SQL SELECT sul database [products.mdb]. L'esecuzione della query crea una nuova vista:

No.
nome
tipo
proprietà
ruolo
1
lblSelect
Etichetta
EnableViewState=false
campo informativo
2
rdAscending
rdDescending
Pulsante di opzione
EnableViewState=false
GroupName=rdSort
consente di scegliere un ordine di ordinamento
3
DataGrid1
DataGrid
EnableViewState=true
AllowPaging=true
ConsentiOrdinamento=vero
tabella che mostra i risultati della selezione
4
lnkRisultati
Pulsante di collegamento
EnableViewState=false
[submit]

Chiameremo questa vista [results]. Contiene il [DataGrid] che visualizzerà i risultati dell'istruzione SQL SELECT. L'utente potrebbe commettere un errore nella query. Alcuni errori gli verranno segnalati nella vista [errors] grazie ai controlli di validazione.

Image

Altri errori gli verranno segnalati dalla vista [errors]:

Image

Viene visualizzata la vista [errors]:

No.
nome
tipo
proprietà
ruolo
1
HTMLErrors
variabile
 
Codice HTML necessario per visualizzare gli errori
3
lnkForm2
Pulsante di collegamento
EnableViewState=false
Pulsante [submit]

Le tre viste dell'applicazione sono tre diversi contenitori (pannelli) all'interno della stessa pagina [main.aspx]. Il codice di presentazione è il seguente:


<%@ 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. Configurazione di DataGrid

Diamo un'occhiata più da vicino all'impaginazione del componente [DataGrid], una funzionalità che incontriamo per la prima volta. Nel codice sopra riportato, questa impaginazione è controllata dai seguenti attributi:


                    <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"
abilita l'impaginazione
PageSize="4"
quattro righe di dati per pagina
NextPageText="Successiva"
Testo del link per passare alla pagina successiva dell'origine dati
PrevPageText="Precedente"
Testo del collegamento per passare alla pagina precedente dell'origine dati

Queste informazioni possono essere inserite direttamente negli attributi del tag <asp:datagrid>. È inoltre possibile utilizzare [WebMatrix]. Nella finestra delle proprietà [DataGrid], fare clic sul collegamento [Generatore di proprietà]:

Image

Viene visualizzata la seguente procedura guidata:

Image

Selezionare l'opzione [Pagination]:

Image

Sopra, vediamo i valori degli attributi di impaginazione per [DataGrid] nel codice di presentazione.

Inoltre, abilitiamo l'ordinamento dei dati su una delle colonne del [DataGrid]. Esistono diversi modi per farlo. Uno consiste nell'impostare la proprietà [AllowSorting=true] nella finestra delle proprietà del [DataGrid]. È anche possibile utilizzare il generatore di proprietà. Indipendentemente dal metodo utilizzato, ciò comporta la presenza dell'attributo [AllowSorting=true] nel tag <asp:DataGrid> del componente:


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

8.6.4. I controller

Il controller [global.asax, global.asax.vb] è il seguente:

[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

All'avvio dell'applicazione (Application_Start), creiamo un oggetto [products] e lo memorizziamo nell'applicazione in modo che sia disponibile per tutte le richieste provenienti da tutti i client. Se durante questa creazione si verifica un'eccezione, questa viene registrata nell'applicazione. La procedura [Application_Start] verrà eseguita una sola volta. Dopodiché, il controller [global.asax] non sarà più coinvolto. Il controller [main.aspx.vb] si occuperà quindi del resto:


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

Quando la pagina viene caricata [Page_Load], controlliamo innanzitutto se l'applicazione è stata in grado di inizializzarsi correttamente. In caso contrario, visualizziamo la vista [errors] senza un collegamento al modulo, poiché tale collegamento non è più necessario. Infatti, se l'applicazione non è stata in grado di inizializzarsi correttamente, può essere visualizzata solo la vista [errors]. Altrimenti, visualizziamo la vista [form] se questa è la prima richiesta del cliente. Per il resto, lasciamo al lettore il compito di comprendere il codice. Ci concentreremo solo su tre procedure: la procedura [btnExecute_Click], che viene eseguita quando l'utente richiede l'esecuzione della query SQL inserita nella vista [form], la procedura [DataGrid1_PageIndexChanged], che viene eseguita quando l'utente utilizza i collegamenti [Next] e [Previous] nel [DataGrid], e la procedura [DataGrid1_SortCommand], che viene eseguita quando l'utente fa clic sull'intestazione di una colonna per ordinare i dati in quell'ordine. L'ordine di ordinamento — ascendente o discendente — è determinato dai due pulsanti di opzione di ordinamento.

Nella procedura [btnExécuter_Click], iniziamo quindi verificando se la pagina è valida o meno. Quando viene eseguita la procedura [btnExécuter_Click], i controlli relativi ai vari controlli di convalida della pagina sono già stati effettuati. Per ogni controllo di convalida sono stati impostati due attributi:

IsValid
impostato su true se i dati verificati sono validi, false in caso contrario
ErrorMessage
il messaggio di errore se i dati verificati non sono validi

Per la pagina stessa, è stato impostato un attributo [IsValid]. È vero solo se tutti i controlli di convalida hanno il loro attributo [IsValid] impostato su true. In caso contrario, deve essere visualizzata la vista [form]. Questa vista contiene i controlli di convalida che visualizzeranno il proprio attributo [errorMessage]. Se la pagina è valida, utilizziamo l'oggetto [products] creato da [Application_Start] per ottenere il [DataSet] corrispondente all'esecuzione della query SQL SELECT. Lo convertiamo in un oggetto [DataView]:


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

Avremmo potuto semplicemente lavorare con il [DataSet] e scrivere:


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

Un oggetto [DataSet] è essenzialmente un insieme di tabelle collegate tra loro da relazioni. Nella nostra applicazione specifica, il [DataSet] ottenuto dalla classe [products] contiene una sola tabella, quella risultante dall'istruzione [select]. Una tabella può essere ordinata, mentre un [DataSet] no; tuttavia, ci interessa ordinare i dati recuperati. Per lavorare con la tabella risultante dall'istruzione [select], avremmo potuto scrivere:


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

L'oggetto [DataTable], sebbene rappresenti una tabella di database, non dispone di un metodo di ordinamento. Per farlo, è necessaria una vista della tabella. Una vista è un oggetto di tipo [DataView]. È possibile avere diverse viste della stessa tabella utilizzando dei filtri. Una tabella ha una vista predefinita, che è quella in cui non sono definiti filtri. Rappresenta quindi l'intera tabella. Questa vista predefinita si ottiene tramite [DataTable.DefaultView]. È possibile ordinare una vista utilizzando la sua proprietà [sort], di cui parleremo più avanti.

Se il recupero del [DataSet] dalla classe [products] ha esito positivo, viene visualizzata la vista [results]; in caso contrario, viene visualizzata la vista [errors]. La vista [results] viene visualizzata tramite la procedura [displayResults], alla quale vengono passati due parametri:

  • il testo da inserire nell'etichetta [lblSelect]
  • il [DataView] da associare a [DataGrid1]

Questo esempio dimostra la grande flessibilità del componente [DataGrid]. È in grado di riconoscere la struttura del [DataView] a cui è associato e di adattarsi ad essa. Infine, la procedura [btnExécuter_Click] memorizza il [DataView] appena ottenuto nella sessione dell’utente in modo che sia disponibile quando l’utente richiede altre pagine dallo stesso [DataView].

La procedura [DataGrid1_PageIndexChanged] viene eseguita quando l'utente fa clic sui collegamenti [Next] e [Previous] nel [DataGrid]. Riceve due parametri:


    Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.) Handles DataGrid1.PageIndexChanged
source
l'oggetto che ha attivato l'evento, in questo caso uno dei collegamenti [Next] o [Previous]
e
informazioni sull'evento. La proprietà e.NewPageIndex è il numero di pagina da visualizzare in risposta alla richiesta del client

Il codice completo per il gestore di eventi è il seguente:


    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

Il componente [DataGrid] ha un attributo [CurrentPageIndex] che indica il numero di pagina che sta visualizzando o che visualizzerà. Assegniamo a questo attributo il valore [NewPageIndex] del parametro [e]. Il [DataGrid] viene quindi associato al [DataView] che è stato salvato nella sessione dalla procedura [btnExécuter_Click].

Ci si potrebbe chiedere se il [DataGrid] necessiti dell'attributo [EnableViewState=true], dato che il suo contenuto viene calcolato dal codice ogni volta che la pagina viene ricaricata. Si potrebbe pensare di no. Tuttavia, se il [DataGrid] ha l'attributo [EnableViewState=false], si osserva che l'evento [DataGrid1.PageIndexChanged] non viene mai attivato. Questo è il motivo per cui abbiamo lasciato [EnableViewState=true]. Sappiamo che ciò fa sì che il contenuto del [DataGrid] venga memorizzato nel campo nascosto della pagina [__VIEWSTATE]. Ciò può rallentare significativamente la pagina se il [DataGrid] è di grandi dimensioni. Se questo rappresenta un problema, è possibile gestire l'impaginazione autonomamente senza utilizzare l'impaginazione automatica del [DataGrid].

La procedura [DataGrid1_SortCommand] viene eseguita quando l'utente fa clic sull'intestazione di una delle colonne visualizzate da [DataGrid] per richiedere l'ordinamento dei dati in base a quella colonna. Riceve due parametri:


    Private Sub DataGrid1_SortCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles DataGrid1.SortCommand
source
l'oggetto che ha attivato l'evento, in questo caso uno dei collegamenti [Next] o [Previous]
e
informazioni sull'evento. La proprietà [e.SortExpression] è il nome della colonna su cui si è cliccato per l'ordinamento

Il codice completo per il gestore di eventi è il seguente:


    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

Recuperiamo il [DataView] visualizzato dal [DataGrid] nella sessione corrente. È stato inserito lì dalla procedura [btnExécuter_Click]. Il componente [DataView] ha una proprietà [Sort] alla quale assegniamo l'espressione di ordinamento. Questa segue la sintassi [select ... order by expr1, expr2, ...], dove ogni [expr] può essere seguito dalla parola chiave [asc] per l'ordinamento ascendente o [desc] per quello discendente. L'espressione [order by] qui utilizzata è [order by column asc/desc]. La proprietà [e.SortExpression] ci fornisce il nome della colonna [DataGrid] su cui è stato cliccato per l'ordinamento. La stringa [asc/desc] viene impostata in base ai valori dei pulsanti di opzione nel gruppo [rdTri]. Una volta impostata l'espressione di ordinamento di [DataView], [DataGrid] viene associato ad essa. Posizioniamo [DataGrid] sulla sua prima pagina.

8.7. Componente DataList e associazione dei dati

Ci concentreremo ora sul componente [DataList]. Offre più opzioni di formattazione rispetto al [DataGrid], ma è meno flessibile. Pertanto, non può adattarsi automaticamente all'origine dati a cui è collegato. Se lo si desidera, tale adattamento deve essere effettuato tramite codice. Se la struttura dell'origine dati è nota in anticipo, questo componente offre opzioni di formattazione che potrebbero renderlo preferibile al [DataGrid].

8.7.1. Applicazione

Per illustrare l'uso di [DataList], creeremo un'applicazione MVC simile alla precedente:

Le tre viste saranno incorporate nel codice di presentazione del controller [main.aspx] come contenitori. Pertanto, questa applicazione ha una singola pagina [main.aspx].

8.7.2. Classi di business

La classe [products] è la stessa di prima.

8.7.3. Le viste

Quando l'utente effettua la sua prima richiesta all'applicazione, vede la seguente vista [results1]:

N.
nome
tipo
proprietà
ruolo
1
RadioButton1
RadioButton2
RadioButton
EnableViewState=false
consente di scegliere uno dei due stili [DataList]
2
btnChanger
Pulsante
EnableViewState=false
Pulsante [submit]
3
DataList1
DataList
EnableViewState=true
Campo di visualizzazione dell'elenco dati

Se l'utente seleziona lo stile n. 2, vedrà la seguente vista [results2]:

N.
nome
tipo
proprietà
ruolo
1
DataList2
DataList
EnableViewState=true
Campo di visualizzazione dell'elenco dati

La vista [errori] indica un problema nell'accesso all'origine dati:

No.
nome
tipo
proprietà
ruolo
1
HTMLErrors
variabile
 
Codice HTML necessario per visualizzare gli errori

Le tre viste dell'applicazione sono tre diversi contenitori (pannelli) all'interno della stessa pagina [main.aspx]. Il codice di presentazione è il seguente:


<%@ 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. Configurazione dei componenti [DataList]

Diamo un'occhiata ai vari attributi di un componente [DataList]. Ce ne sono molti, ma qui ne tratteremo solo una piccola selezione. È possibile definire fino a sette modelli di visualizzazione all'interno di un [DataList]:

HeaderTemplate
Modello di intestazione [DataList]
ItemTemplate
Modello per le righe che visualizzano gli elementi nell'elenco dati associato. È richiesto solo questo modello.
AlternatingItemTemplate
Per distinguere visivamente gli elementi visualizzati in successione, è possibile utilizzare due modelli: ItemTemplate per l'elemento n, AlternatingItemTemplate per l'elemento n+1
SelectedItemTemplate
Modello per l'elemento selezionato nel [DataList]
SeparatorTemplate
Modello per il separatore tra due elementi nel [DataList]
EditItemTemplate
Un [DataList] consente di modificare i valori visualizzati. [EditItemTemplate] è il modello per un elemento nel [DataList] che si trova attualmente in modalità "modifica"
FooterTemplate
Modello per il piè di pagina del [DataList]

Il componente [DataList1] è stato creato con [WebMatrix]. Nella finestra delle proprietà è stato selezionato il collegamento [AutoFormat]:

1234567

Sopra, lo schema [Colore 5] genererà un [DataList] con stili per i seguenti modelli: HeaderTemplate (1), ItemTemplate (2, 6), AlternatingTemplate (3, 5), SelectedItemTemplate (4), FooterTemplate (7). Il codice generato è il seguente:


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

Non sono stati definiti modelli. Spetta a noi farlo. Definiamo i seguenti modelli:

HeaderTemplate

                        <HeaderTemplate>
                            Contenuto della tabella [list] nel database [products]
                        </HeaderTemplate>
ItemTemplate

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

Ricordate che il modello [ItemTemplate] viene utilizzato per visualizzare gli elementi provenienti dall'origine dati collegata a [DataList]. Questa origine dati è una raccolta di righe di dati, ciascuna delle quali contiene uno o più valori. La riga corrente nell'origine dati è rappresentata dall'oggetto [Container.DataItem]. Tale riga presenta delle colonne. [Container.DataItem("col1")] è il valore della colonna "col1" nella riga corrente. Per includere questo valore nel codice di presentazione, scriviamo <%# Container.DataItem("col") %>. A volte, vogliamo visualizzare un elemento della riga corrente in un formato speciale. In questo caso, vogliamo visualizzare la colonna "price" della riga corrente in euro. Utilizziamo la funzione [DataBinder.Eval], che accetta tre parametri:

  • la riga corrente [Container.DataItem]
  • il nome della colonna da formattare
  • la stringa di formattazione nel formato {0:format}, dove [format] è uno dei formati accettati dal metodo [string.format].

Pertanto, il codice <%# DataBinder.Eval(Container.DataItem,"price",{0:C}) %> visualizzerà la colonna [price] della riga corrente in formato valuta (formato C=Currency).

Avremo quindi un [DataList] simile a questo:

Image

Sopra, i dati sono stati disposti con quattro elementi per riga. Ciò si ottiene utilizzando i seguenti attributi [DataList]:

RepeatDirection
Horizontal
RepeatColumns
numero desiderato di colonne

In definitiva, il codice per [DataList1] è quello presentato nel frammento di codice sopra riportato. Lasciamo al lettore il compito di studiare il codice di presentazione per [DataList2]. Come per il componente [DataGrid], la maggior parte delle proprietà di [DataList] può essere impostata utilizzando una procedura guidata di [WebMatrix]. Per farlo, utilizzare il collegamento [Property Generator] nella finestra delle proprietà di [DataList]:

8.7.5. I controller

Il controller [global.asax, global.asax.vb] è il seguente:

[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

All'avvio dell'applicazione (Application_Start), creiamo un [dataset] dalla classe di business [products] e lo aggiungiamo all'applicazione in modo che sia disponibile per tutte le richieste provenienti da tutti i client. Se durante questa creazione si verifica un'eccezione, questa viene registrata nell'applicazione. La procedura [Application_Start] verrà eseguita una sola volta. Dopodiché, il controller [global.asax] non sarà più coinvolto. Il controller [main.aspx.vb] gestirà quindi il lavoro:


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. Componente Repeater e associazione dati

Il componente [Repeater] consente di ripetere il codice HTML per ogni elemento di un elenco di dati. Supponiamo di voler visualizzare un elenco di errori nel seguente formato:

Image

Abbiamo già affrontato questo problema e lo abbiamo risolto includendo una variabile nel codice di presentazione nella forma <% =erreursHTML %>, dove il valore di erreursHTML viene calcolato dal controller. Questo valore contiene codice HTML, in particolare quello di un elenco. Lo svantaggio è che se si desidera modificare la presentazione di questo elenco HTML, è necessario accedere alla sezione del controller, il che va contro la separazione tra controller e presentazione. Il componente [Repeater] fornisce una soluzione. Come con [DataList], possiamo definire <HeaderTemplate> per l'intestazione, <ItemTemplate> per l'elemento corrente nell'elenco dei dati e <FooterTemplate> per la fine dei dati. In questo caso, potremmo avere la seguente definizione per il componente [Repeater]:

            <asp:Repeater id="Repeater1" runat="server" EnableViewState="False">
                <HeaderTemplate>
                    Les erreurs suivantes se sont produites :
                    <ul>
                </HeaderTemplate>
                <ItemTemplate>
                    <li>
                        <%# Container.DataItem %>
                    </li>
                </ItemTemplate>
                <FooterTemplate>
                    </ul>
                </FooterTemplate>
            </asp:Repeater>

Si noti che [Container.DataItem] rappresenta una riga di dati se l'origine dati ha più colonne. Rappresenta un singolo punto dati se l'origine ha una sola colonna. Questo sarà il caso in questione. Ad esempio, stiamo realizzando la seguente applicazione:

Image

Il codice del layout della pagina è il seguente:

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

Il codice di controllo è il seguente:

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

Alla prima richiesta del client, associamo un oggetto [ArrayList] al componente [Repeater], che dovrebbe rappresentare un elenco di errori.

8.9. Applicazione

Qui, riprendiamo un'applicazione precedentemente implementata con componenti lato server. L'applicazione consente agli utenti di simulare il calcolo delle imposte. Si basa su una classe [impot], che non riprenderemo qui. Questa classe richiede dati che recupera da una fonte di dati OLEDB. Per questo esempio, useremo una fonte di dati ACCESS. In questa versione introduciamo le seguenti nuove funzionalità:

  • l'uso di componenti di convalida per verificare la validità dei dati
  • l'uso di componenti server collegati a origini dati per la visualizzazione dei risultati

8.9.1. La struttura MVC dell'applicazione

La struttura MVC dell'applicazione è la seguente:

Le tre viste saranno incorporate nel codice di presentazione del controller [main.aspx] come contenitori. Pertanto, questa applicazione ha una singola pagina [main.aspx].

8.9.2. Le viste dell'applicazione

La vista [form] è il modulo per l'inserimento delle informazioni utilizzate per calcolare l'imposta di un utente:

Image

L'utente compila il modulo:

Image

Utilizza il pulsante [Invia] per richiedere il calcolo dell'imposta. Viene visualizzata la seguente vista [simulazioni]:

Image

Torna al modulo tramite il link in alto. Lo trova nello stato in cui lo ha compilato. Potrebbe commettere errori nell'inserimento dei dati:

Image

Questi vengono segnalati nella vista [form]:

Image

L'utente corregge quindi i propri errori. Può eseguire nuove simulazioni:

Image

Viene visualizzata la schermata [simulazioni] con una simulazione aggiuntiva:

Image

Infine, se la fonte dei dati non è disponibile, l'utente riceve una notifica nella vista [errori]:

Image

8.9.2.1. Il codice di presentazione

Ricordate che la pagina [main.aspx] riunisce tutte le viste. Si tratta di un unico modulo con tre contenitori:

  • [panelform] per la vista [form]
  • [panelerrors] per la vista [errori]
  • [panelsimulations] per la vista [simulations]

Ora descriveremo in dettaglio i componenti di questi tre contenitori. Il contenitore [panelform] ha la seguente rappresentazione visiva:

N.
nome
tipo
proprietà
ruolo
0
pannello
Pannello
 
vista del modulo
1
rdSì
No
Pulsante di opzione
NomeGruppo=rdmarie
pulsanti di opzione
2
cvMarie
Validatore personalizzato
ErrorMessage=Non hai specificato il tuo stato civile
EnableClientScript=false
verifica che il programma client abbia inviato lo stato civile previsto
3
txtChildren
Casella di testo
 
numero di figli
4
rfvChildren
Validatore campo obbligatorio
Messaggio di errore=Inserisci il numero di figli
ControlToValidate=txtChildren
verifica che il campo [txtChildren] non sia vuoto
5
rvChildren
RangeValidator
ErrorMessage=Inserisci un numero compreso tra 1 e 30
ControlToValidate=txtChildren
verifica che il campo [txtChildren] rientri nell'intervallo [1,30]
6
txtSalary
Casella di testo
EnableViewState=true
stipendio annuale
7
rfvStipendio
ValidatoreCampoObbligatorio
ControlToValidate=txtSalary
Messaggio di errore=Inserisci l'importo del tuo stipendio
verifica che il campo [txtSalary] non sia vuoto
8
revSalary
Validatore di espressioni regolari
ControlToValidate=txtSalary
Messaggio di errore=Stipendio non valido
EspressioneRegolare=\s*\d+\s*
verifica che il campo [txtSalary] sia una sequenza di cifre
9
btnCalculate
Pulsante
ValidationTriggers=true
Pulsante [submit] nel modulo - avvia il calcolo delle imposte
10
btnClear
Pulsante
Convalida=false
Pulsante [Invia] del modulo - cancella il modulo

Potresti essere sorpreso dal controllo [cvMarié], che verifica che l'utente abbia selezionato uno dei due pulsanti di opzione. Ciò è necessario perché non c'è modo di essere certi che l'utente stia utilizzando il modulo inviato dal server. Poiché nulla può garantirlo, dobbiamo verificare tutti i parametri inviati. Notate anche l'attributo [CausesValidation=false] del pulsante [btnEffacer]. Quando l'utente fa clic su questo pulsante, i dati inviati non devono essere controllati poiché verranno ignorati.

Tutti i componenti nel contenitore hanno la proprietà [EnableViewState=false] tranne [panelForm, txtEnfants, txtSalaire, rdOui, rdNon]. Il contenitore [panelerreurs] ha la seguente rappresentazione visiva:

No.
nome
tipo
proprietà
ruolo
0
pannello di errore
Pannello
EnableViewState=false
vista di errore
1
rptErrors
Ripetitore
EnableViewState=false
visualizza un elenco di errori

Il componente [rptErrors] è definito come segue:


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

L'elenco di dati associato al componente [rptErrors] sarà un oggetto [ArrayList] contenente un elenco di messaggi di errore. Pertanto, come mostrato sopra, <%# Container.Dataitem%> fa riferimento al messaggio di errore corrente. Il contenitore [panelsimulations] ha la seguente rappresentazione visiva:

N.
nome
tipo
proprietà
ruolo
0
simulazioni su pannello
Pannello
EnableViewState=false
vista simulazione
2
lnkForm2
LinkButton
EnableViewState=false
collegamento al modulo
1
dgSimulations
DataGrid
EnableViewState=false
responsabile della visualizzazione delle simulazioni inserite in un oggetto [DataTable]

Il componente [dgSimulations] è configurato come segue in [Webmatrix]. Nella finestra delle proprietà di [dgSimulations], selezioniamo il collegamento [AutoFormat] e scegliamo lo schema [Color 3]:

Quindi, sempre nella finestra delle proprietà [dlgSimulations], selezioniamo il collegamento [Property Generator]. Richiediamo che vengano visualizzate le intestazioni delle colonne:

Image

Selezioniamo l'opzione [Columns] per definire le quattro colonne del [DataGrid]:

Image

Deselezioniamo l'opzione [Crea colonne automaticamente...]. Definiremo noi stessi le quattro colonne del [DataGrid]. Questo sarà associato a un oggetto [DataTable] contenente quattro colonne denominate rispettivamente "married", "children", "salary" e "tax". Per creare una colonna nel [DataGrid], selezioniamo l'opzione [Colonne correlate] e utilizziamo il pulsante di creazione come mostrato sopra. Viene creata una colonna e possiamo definirne le proprietà. La prima colonna della tabella di simulazione conterrà la colonna "sposato" dell'oggetto [DataTable] che sarà associato al [DataGrid]. Questa prima colonna del [DataGrid] è definita come segue nella procedura guidata:

Image

Testo dell'intestazione
titolo della colonna, in questo caso "Sposato"
Campo dati
Nome della colonna nell'origine dati che verrà visualizzata da questa colonna del [DataGrid]. In questo caso, si tratta della colonna "married" del [DataTable].

La seconda colonna è definita come segue:

Testo dell'intestazione
Figli
Campo dati
figli

La terza colonna è definita come segue:

Testo dell'intestazione
Stipendio annuo
Campo dati
stipendio
Espressione di formattazione
{0:C} - visualizzazione della valuta per ottenere il simbolo dell'euro

La quarta colonna è definita come segue:

Testo dell'intestazione
Importo delle imposte
Campo dati
imposta
Espressione di formattazione
{0:C} - visualizzazione della valuta per ottenere il simbolo dell'euro

Inseriamo il codice di presentazione e il codice di controllo in due file separati. Il primo sarà in [main.aspx] e il secondo in [main.aspx.vb]. Il codice per [main.aspx] è il seguente:


<%@ 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. Il codice di controllo dell'applicazione

Il codice di controllo dell'applicazione è distribuito tra i file [global.asax.vb] e [main.aspx.vb]. Il file [global.asax] è definito come segue:

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

Il file [global.asax.vb] è il seguente:


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

All'avvio dell'applicazione (prima richiesta effettuata all'applicazione), viene eseguita la procedura [Application_Start]. Essa tenta di creare un oggetto di tipo [tax] recuperando i relativi dati da una fonte OLEDB. Si invita il lettore a consultare il Capitolo 5, dove è stata definita questa classe, qualora l'avesse dimenticata. La creazione dell'oggetto [impot] potrebbe fallire se la fonte dati non è disponibile. In questo caso, l'errore viene memorizzato nell'applicazione in modo che tutte le richieste successive sappiano che non è stato possibile inizializzarlo correttamente. Se la creazione ha esito positivo, anche l'oggetto [impot] creato viene memorizzato nell'applicazione. Verrà utilizzato da tutte le richieste di calcolo delle imposte. Quando un client effettua la sua prima richiesta, la procedura [Application_Start] crea una sessione per lui. Questa sessione ha lo scopo di memorizzare le varie simulazioni di calcolo delle imposte che il cliente effettuerà. Queste saranno memorizzate in un oggetto [DataTable] associato alla chiave di sessione "simulations". All'avvio della sessione, questa chiave viene associata a un oggetto [DataTable] vuoto la cui struttura è stata definita. Le informazioni richieste dall'applicazione sono inserite nel suo file di configurazione [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>

La chiave [connectionString] specifica la stringa di connessione alla fonte OLEDB. Il resto del codice di controllo si trova 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

Il primo evento gestito dal codice è [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

Prima di elaborare la richiesta, verifichiamo che l'applicazione sia stata inizializzata correttamente. In caso contrario, visualizziamo la vista [errors] utilizzando la procedura [displayErrors]. Se questa è la prima richiesta (IsPostBack=false), visualizziamo la vista [form] utilizzando [displayForm].

La procedura che visualizza la vista [errors] è la seguente:


    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

La procedura riceve come parametro un elenco di messaggi di errore in [errors] di tipo [ArrayList]. È sufficiente associare questa fonte dati al componente [rptErrors] responsabile della visualizzazione.

La procedura che visualizza la vista [form] è la seguente:


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

Questa procedura rende semplicemente visibile il contenitore [panelform]. I componenti vengono visualizzati con il loro valore inviato o precedente (VIEWSTATE).

Quando l'utente fa clic sul pulsante [Calculate] nella vista [form], viene inviata una richiesta POST a [main.aspx]. Viene eseguita la procedura [Page_Load], seguita dalla procedura [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

La procedura inizia verificando la validità della pagina. Ricordiamo che quando viene eseguita la procedura [btnCalculate_Click], i controlli di convalida hanno completato il loro lavoro e l'attributo [IsValid] della pagina è stato impostato. Se la pagina non è valida, viene visualizzata nuovamente la vista [form] e la procedura termina. I componenti di convalida della vista [form] il cui attributo [IsValid] è impostato su [false] visualizzeranno il loro attributo [ErrorMessage]. Se la pagina è valida, l'importo dell'imposta viene calcolato utilizzando l'oggetto [tax] memorizzato nell'applicazione all'avvio. Questa nuova simulazione viene aggiunta all'elenco delle simulazioni già eseguite e memorizzate nella sessione.

Infine, la vista [simulations] viene visualizzata dalla seguente procedura [displaySimulations]:


    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

La procedura ha due parametri:

  • un elenco di simulazioni in [simulations] di tipo [DataTable]
  • un testo di collegamento in [link]

L'origine dati [simulations] è associata al componente responsabile della sua visualizzazione. Il testo del collegamento è inserito nella proprietà [Text] dell'oggetto [LinkButton] nella vista.

Quando l'utente fa clic sul pulsante [Delete] nella vista [form], viene eseguita la procedura [btnDelete_click] (sempre dopo [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

Il codice sopra riportato è talmente semplice da non necessitare di alcun commento. Dobbiamo ancora gestire i clic sui collegamenti delle viste [errori] e [simulazioni]:


    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

Entrambe le procedure visualizzano semplicemente la vista [form]. Sappiamo che i campi in questa vista riceveranno un valore che è o il valore inviato per essi o il loro valore precedente. Poiché, in questo caso, la richiesta POST del client non invia alcun valore per i campi del modulo, questi torneranno ai loro valori precedenti. Il modulo viene quindi visualizzato con i valori inseriti dall'utente.

8.9.4. Test

Tutti i file richiesti dall'applicazione sono collocati in una cartella denominata <application-path>:
La cartella [bin] contiene la DLL con le classi [impot], [impotsData] e [impotsOLEDB] richieste dall'applicazione:

Se lo si desidera, il lettore può fare riferimento al Capitolo 5, che spiega come creare il file [impot.dll] menzionato sopra. Una volta fatto ciò, il server Cassini viene avviato con i parametri (<percorso-applicazione>,/impots6). Si accede quindi all'URL [http://impots6/main.aspx] utilizzando un browser:

Image

Se si rinomina il file ACCESS [impots.mdb] in [impots1.mdb], verrà visualizzata la pagina seguente:

Image

8.9.5. Conclusione

Abbiamo un'applicazione MVC che utilizza solo componenti lato server. L'uso di questi componenti ci ha permesso di separare completamente il livello di presentazione dell'applicazione dal suo livello di controllo. Ciò non era possibile fino ad ora, poiché il codice di presentazione conteneva in precedenza variabili calcolate dal codice di controllo, i cui valori includevano codice HTML. Ora non è più così.