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:

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:

ASP.NET offre componenti denominati componenti di convalida che consentono di verificare quanto segue:
Componente | Ruolo |
verifica che un campo non sia vuoto | |
confronta due valori tra loro | |
verifica che un valore rientri tra due limiti | |
verifica che un campo corrisponda a un'espressione regolare | |
consente allo sviluppatore di definire le proprie regole di convalida: questo componente potrebbe sostituire tutti gli altri | |
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 | Casella di testo | EnableViewState=true | campo di immissione | |
2 | ValidatoreCampoObbligatorio | EnableViewState=false EnableClientScript=true Messaggio di errore=Il campo [nome] è obbligatorio | componente di convalida | |
3 | 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:
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. | |
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. | |
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]:

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" />
</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:

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:

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

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:

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

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

No. | nome | tipo | proprietà | ruolo |
1 | Elenco a discesa | EnableViewState=true | elenco a discesa | |
2 | Elenco a discesa | EnableViewState=true | elenco a discesa | |
3 | CompareValidator | EnableViewState=false AbilitaScriptClient=true Messaggio di errore=Scelta non valida 1 ValoreDaConfrontare=? Operatore=Non uguale Tipo=stringa | componente di convalida | |
4 | CompareValidator | EnableViewState=false EnableClientScript=true Messaggio di errore=Scelta non valida 2 ControlToCompare=? Operatore=Non uguale Tipo=stringa | componente di validazione | |
5 | 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:
campo il cui contenuto deve essere convalidato dal componente | |
Booleano - se vero, indica che il contenuto del campo precedente deve essere convalidato anche sul lato client | |
il messaggio di errore che il componente deve visualizzare se viene rilevato un errore | |
il valore con cui deve essere confrontato il valore del campo [ControlToValidate] | |
il componente con cui deve essere confrontato il valore del campo [ControlToValidate] | |
operatore di confronto tra i due valori | |
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:

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

No. | nome | tipo | proprietà | ruolo |
Elenco a discesa | EnableViewState=true | elenco a discesa | ||
CompareValidator | EnableViewState=false AbilitaScriptClient=true ErrorMessage=Diploma non valido Operatore=Non uguale ValueToCompare=? Tipo=Stringa | componente di validazione del controllo [1] | ||
Casella di testo | EnableViewState=false | campo di immissione | ||
Validatore personalizzato | EnableViewState=false EnableClientScript=true ErrorMessage=Specifiche del titolo di studio non valide ClientValidationFunction=chkOtherDegree | componente di convalida per il campo [3] | ||
Casella di testo | EnableViewState=false | campo di immissione | ||
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] | ||
ValidatoreCampoObbligatorio | EnableViewState=false AbilitaScriptClient=vero ErrorMessage=È necessario specificare l'anno di laurea ControlToValidate=txtAnDiplome | componente di convalida campo [5] | ||
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:
campo il cui valore deve essere convalidato dal componente | |
Booleano - se vero, indica che anche il contenuto del campo precedente deve essere convalidato sul lato client | |
il messaggio di errore che il componente deve visualizzare se viene rilevato un errore | |
valore minimo del campo da convalidare | |
valore massimo del campo da verificare | |
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:
Il campo il cui valore deve essere convalidato dal componente | |
Booleano - se true, indica che il contenuto del campo precedente deve essere convalidato anche sul lato client | |
il messaggio di errore che il componente deve visualizzare se viene rilevato un errore | |
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îtrise">Maî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]:

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

No. | nome | tipo | proprietà | ruolo |
Casella di testo | EnableViewState=true | campo di immissione | ||
ValidatoreCampoObbligatorio | ControlToValidate=txtMel Display=Dynamic | componente di convalida controllo [1] | ||
Validatore di espressioni regolari | ControlToValidate=txtMel Visualizza=Dinamico | componente di convalida controllo [1] | ||
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:
campo il cui valore deve essere convalidato dal componente | |
booleano - se vero, indica che anche il contenuto del campo precedente deve essere convalidato sul lato client | |
il messaggio di errore che il componente deve visualizzare se viene rilevato un errore | |
l'espressione regolare con cui verrà confrontato il contenuto di [ControlToValidate] | |
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:

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

No. | nome | tipo | proprietà | ruolo |
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 | 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]:

1
No. | Nome | tipo | proprietà | ruolo |
1 | 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:

Il pulsante [Invia] ci fornisce la seguente risposta:

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

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

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:
<option value="V">T</option> | |
<input type="checkbox" value="V">T | |
<input type="radio" value="V">T |
Un componente [ListControl] è associato a un'origine dati tramite le seguenti proprietà:
una fonte dati [Array], [ArrayList], [DataTable], [DataSet], [HashTable], ... | |
se l'origine dati è un [DataSet], rappresenta il nome della tabella da utilizzare come origine dati. L'origine dati effettiva è quindi una tabella. | |
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] | |
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:
[ListControl].DataSource=A I campi [Text] e [Value] degli elementi [ListControl] avranno i valori degli elementi in A | |
[ListControl].DataSource=AL I campi [Text] e [Value] degli elementi [ListControl] avranno i valori degli elementi in AL | |
[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 | |
[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" | |
[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 |
Array | |
ArrayList | |
DataTable | |
DataSet | |
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:
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:
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:
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]:
una fonte dati [Array], [ArrayList], [DataTable], [DataSet], ... | |
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:

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

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:

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

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:

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:
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 | DataGrid | EnableViewState=true | visualizza un'origine dati S | |
2 | DataGrid | EnableViewState=true | visualizza la stessa origine dati S di [DataGrid1] | |
3 | 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:

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:

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 | DataGrid | EnableViewState=true | visualizza un'origine dati S | |
2 | DataGrid | EnableViewState=false | visualizza la stessa origine dati S di [DataGrid1] | |
3 | Pulsante | EnableViewState=false | Pulsante [submit] | |
4 | 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:

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 :
<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:
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:
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:
per memorizzare il nuovo valore di [numRequest3] nella sessione. Invece, dovresti scrivere:
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:

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 | Casella di testo | EnableViewState=true | campo di immissione query di selezione | |
2 | ValidatoreCampoObbligatorio | EnableViewState=false | verifica la presenza di 1 | |
3 | TextBox | EnableViewState=true | campo di immissione - specifica il numero di righe di dati da visualizzare per pagina dei risultati | |
4 | ValidatoreCampoObbligatorio | EnableViewState=false | verifica la presenza di 3 | |
5 | RangeValidator | EnableViewState=false | verifica che (3) sia compreso nell'intervallo [1,30] | |
6 | 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 | Etichetta | EnableViewState=false | campo informativo | |
2 | Pulsante di opzione | EnableViewState=false GroupName=rdSort | consente di scegliere un ordine di ordinamento | |
3 | DataGrid | EnableViewState=true AllowPaging=true ConsentiOrdinamento=vero | tabella che mostra i risultati della selezione | |
4 | 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.

Altri errori gli verranno segnalati dalla vista [errors]:

Viene visualizzata la vista [errors]:
![]() |
No. | nome | tipo | proprietà | ruolo |
1 | variabile | Codice HTML necessario per visualizzare gli errori | ||
3 | 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> Exemple : select id, nom, prix from LISTE
</P>
<P>
<asp:TextBox id="txtSelect" runat="server" Columns="60"></asp:TextBox></P>
<P>
<asp:RequiredFieldValidator id="RequiredFieldValidator1" runat="server" EnableViewState="False" EnableClientScript="False"
ErrorMessage="Indiquez la requête [select] à exécuter" ControlToValidate="txtSelect" Display="Dynamic"></asp:RequiredFieldValidator></P>
<P>Nombre de lignes par page :
<asp:TextBox id="txtPages" runat="server" Columns="3"></asp:TextBox></P>
<P>
<asp:RequiredFieldValidator id="RequiredFieldValidator2" runat="server" EnableViewState="False" EnableClientScript="False"
ErrorMessage="Indiquez le nombre de lignes par page désirées" ControlToValidate="txtPages" Display="Dynamic"></asp:RequiredFieldValidator>
<asp:RangeValidator id="RangeValidator1" runat="server" EnableViewState="False" EnableClientScript="False"
ErrorMessage="Vous devez indiquer un nombre entre 1 et 30" ControlToValidate="txtPages" Display="Dynamic"
Type="Integer" MinimumValue="1" MaximumValue="30"></asp:RangeValidator></P>
<P>
<asp:Button id="btnExécuter" runat="server" EnableViewState="False" Text="Exécuter"></asp:Button></P>
</asp:panel>
<asp:Panel id="vueRésultats" runat="server">
<P>Résultats de la requête
<asp:Label id="lblSelect" runat="server"></asp:Label></P>
<P>Tri
<asp:RadioButton id="rdCroissant" runat="server" Text="croissant" Checked="True" GroupName="rdTri"></asp:RadioButton>
<asp:RadioButton id="rdDécroissant" runat="server" Text="décroissant" GroupName="rdTri"></asp:RadioButton></P>
<P>
<asp:DataGrid id="DataGrid1" runat="server" BorderColor="#CC9966" BorderStyle="None" BorderWidth="1px"
BackColor="White" CellPadding="4" AllowPaging="True" PageSize="4" AllowSorting="True">
<SelectedItemStyle Font-Bold="True" ForeColor="#663399" BackColor="#FFCC66"></SelectedItemStyle>
<ItemStyle ForeColor="#330099" BackColor="White"></ItemStyle>
<HeaderStyle Font-Bold="True" ForeColor="#FFFFCC" BackColor="#990000"></HeaderStyle>
<FooterStyle ForeColor="#330099" BackColor="#FFFFCC"></FooterStyle>
<PagerStyle NextPageText="Suivant" PrevPageText="Précédent" HorizontalAlign="Center"
ForeColor="#330099" BackColor="#FFFFCC"></PagerStyle>
</asp:DataGrid></P>
<P>
<asp:LinkButton id="lnkRésultats" runat="server" EnableViewState="False">Retour au formulaire</asp:LinkButton></P>
</asp:Panel>
<asp:Panel id="vueErreurs" runat="server">
<P>Les erreurs suivantes se sont produites :</P>
<% =erreursHTML %>
<P>
<asp:LinkButton id="lnkErreurs" runat="server" EnableViewState="False">Retour vers le formulaire</asp:LinkButton></P>
</asp:Panel>
</form>
</body>
</HTML>
8.6.3. 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écédent" ...></PagerStyle>
</asp:DataGrid></P>
abilita l'impaginazione | |
quattro righe di dati per pagina | |
Testo del link per passare alla pagina successiva dell'origine dati | |
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à]:

Viene visualizzata la seguente procedura guidata:

Selezionare l'opzione [Pagination]:

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]
[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:
impostato su true se i dati verificati sono validi, false in caso contrario | |
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
l'oggetto che ha attivato l'evento, in questo caso uno dei collegamenti [Next] o [Previous] | |
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
l'oggetto che ha attivato l'evento, in questo caso uno dei collegamenti [Next] o [Previous] | |
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 | RadioButton | EnableViewState=false | consente di scegliere uno dei due stili [DataList] | |
2 | Pulsante | EnableViewState=false | Pulsante [submit] | |
3 | 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 | 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 | 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]:
Modello di intestazione [DataList] | |
Modello per le righe che visualizzano gli elementi nell'elenco dati associato. È richiesto solo questo modello. | |
Per distinguere visivamente gli elementi visualizzati in successione, è possibile utilizzare due modelli: ItemTemplate per l'elemento n, AlternatingItemTemplate per l'elemento n+1 | |
Modello per l'elemento selezionato nel [DataList] | |
Modello per il separatore tra due elementi nel [DataList] | |
Un [DataList] consente di modificare i valori visualizzati. [EditItemTemplate] è il modello per un elemento nel [DataList] che si trova attualmente in modalità "modifica" | |
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:
| |
|
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:

Sopra, i dati sono stati disposti con quattro elementi per riga. Ciò si ottiene utilizzando i seguenti attributi [DataList]:
Horizontal | |
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]
[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:

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:

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:

L'utente compila il modulo:

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

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

Questi vengono segnalati nella vista [form]:

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

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

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

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 |
Pannello | vista del modulo | |||
Pulsante di opzione | NomeGruppo=rdmarie | pulsanti di opzione | ||
Validatore personalizzato | ErrorMessage=Non hai specificato il tuo stato civile EnableClientScript=false | verifica che il programma client abbia inviato lo stato civile previsto | ||
Casella di testo | numero di figli | |||
Validatore campo obbligatorio | Messaggio di errore=Inserisci il numero di figli ControlToValidate=txtChildren | verifica che il campo [txtChildren] non sia vuoto | ||
RangeValidator | ErrorMessage=Inserisci un numero compreso tra 1 e 30 ControlToValidate=txtChildren | verifica che il campo [txtChildren] rientri nell'intervallo [1,30] | ||
Casella di testo | EnableViewState=true | stipendio annuale | ||
ValidatoreCampoObbligatorio | ControlToValidate=txtSalary Messaggio di errore=Inserisci l'importo del tuo stipendio | verifica che il campo [txtSalary] non sia vuoto | ||
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 | ||
Pulsante | ValidationTriggers=true | Pulsante [submit] nel modulo - avvia il calcolo delle imposte | ||
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 |
Pannello | EnableViewState=false | vista di errore | ||
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 |
Pannello | EnableViewState=false | vista simulazione | ||
LinkButton | EnableViewState=false | collegamento al modulo | ||
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:

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

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:

titolo della colonna, in questo caso "Sposato" | |
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:
Figli | |
figli |
La terza colonna è definita come segue:
Stipendio annuo | |
stipendio | |
{0:C} - visualizzazione della valuta per ottenere il simbolo dell'euro |
La quarta colonna è definita come segue:
Importo delle imposte | |
imposta | |
{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é" HeaderText="Marié"></asp:BoundColumn>
<asp:BoundColumn DataField="enfants" HeaderText="Enfants"></asp:BoundColumn>
<asp:BoundColumn DataField="salaire" HeaderText="Salaire annuel" DataFormatString="{0:C}"></asp:BoundColumn>
<asp:BoundColumn DataField="impôt" HeaderText="Montant de l'impôt" DataFormatString="{0:C}"></asp:BoundColumn>
</Columns>
<PagerStyle HorizontalAlign="Center" ForeColor="#8C4510" Mode="NumericPages"></PagerStyle>
</asp:DataGrid></P>
<P>
<asp:LinkButton id="lnkForm2" runat="server" EnableViewState="False">Retour au formulaire</asp:LinkButton></P>
</asp:panel>
</FORM>
</body>
</HTML>
8.9.3. 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:
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:

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

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






















