Sunteți pe pagina 1din 192

Dveloppement WEB avec ASP.NET 1.

1 VOLUME 2

serge.tahe@istia.univ-angers.fr avril 2004

Introduction
Le volume 1 du cours ASP.NET a prsent les notions fondamentales de la programmation web, celles que l'on retrouve tout le temps, quelque soit la technologie de dveloppement utilise (Java, Php, Asp.net). Nous avons en particulier prsent la mthode MVC (Modle, Vue, Contrleur) prconise pour tout dveloppement web. Le volume 1 s'est attard essentiellement sur les parties Modle et Contrleur et peu sur les vues. Celles-ci taient construites sans fioritures avec le langage HTML. ASP.NET amne avec lui une bibliothque de composants permettant : la conception d'interfaces web plus riches sans dveloppement excessif la conception du contrleur de l'application comme un ensemble de gestionnaire d'vnements lis aux composants des vues L'criture d'une application web devient similaire celle d'une application windows o on dessine des interfaces avant d'crire les gestionnaires des vnements que celles-ci peuvent gnrer. Cependant, lorsqu'on essaie de raccrocher ce mode de conception au modle MVC, on s'aperoit qu'ils ne vont pas ensemble. Il n'est pas possible d'avoir un unique contrleur par lequel passent toutes les requtes des clients si l'application utilise des formulaires construits avec des composants ASP.NET. C'est fcheux car il nous faut alors choisir entre deux modes de conception ayant chacun un intrt. On peut cependant montrer qu'il est possible d'crire des application utilisant les composants riches d'ASP.NET si on accepte d'avoir plusieurs contrleurs au lieu d'un seul. Cette solution est souvent acceptable. Ce document expose comment construire des vues l'aide des composants ASP.NET appels composants serveur. C'est un long travail. Il existe beaucoup de composants et ceratins sont trs complexes. Ce volume 2 consacr aux seules vues est ainsi important en taille que celui consacr aux notions fondamentales. C'est de plus compltement propritaire puisque on ne retrouve pas les techniques qui vont tre exposes ni en Java ni en PHP. C'est pourquoi nous avons choisi de les isoler dans un volume part considrant qu'il fallait d'abord mariser les notions fondamentales de tout dveloppement web avant de s'intresser aux problmes de construction de vues. Le thme des composants serveur ASP.NET est tellement vaste que ce volume 2 n'arrive pas l'puiser. Les composants HTML serveur n'ont pas t exposs. Ils le seront peut-tre ultrieurement. Ce document comporte peut-tre encore des erreurs : toute suggestion constructive est la bienvenue l'adresse serge.tahe@istia.univangers.fr. Serge Tah Juin 2004

1 Composants serveur ASP - 1


1.1 Introduction

Nous dcrivons dans ce chapitre, la technologie prconise dans ASP.NET pour construire l'interface utilisateur. Nous savons qu'il y a deux phases bien distinctes dans le traitement d'une page .aspx par le serveur web : 1. 2. il y a tout d'abord excution du contrleur de la page. Celui-ci est constitu par du code situ soit dans la page .aspx ellemme (solution WebMatrix) soit dans un fichier part (solution Visual Studio.NET). puis le code de prsentation de la page .aspx est excut pour tre transform en code HTML envoy au client.

contrleur de page Client

prsentation page

Serveur

ASP.NET offre trois bibliothques de balises pour crire le code de prsentation de la page : 1. les balises HTML classiques. C'est ce que nous avons utilis jusqu' maintenant. 2. les balises HTML serveur 3. les balises webforms Quelque soit la bibliothque de balises utilise, le rle du contrleur de page ne change pas. Il doit calculer la valeur des paramtres dynamiques apparaissant dans le code de prsentation. Jusqu' maintenant, ces paramtres dynamiques taient simples : c'taient des objets de type [String]. Ainsi si dans le code de prsentation on a une balise <%=nom%> : le contrleur de page dclare une variable [nom] de type [String] et calcule sa valeur lorsque le contrleur de page a termin son travail et que le code de prsentation est excut pour gnrer la rponse HTML, la balise <%=nom%> est remplace par la valeur calcule par le code de contrle On sait que le dcoupage contrleur/prsentation est arbitraire et qu'on peut mlanger code contrleur et code de prsenttaion dans la mme page. Nous avons expliqu pourquoi cette mthode tait dconseille et nous continuerons respecter le dcoupage contrle/prsentation. Les balises [HTML serveur] et [WebForms] permettent d'introduire dans le code de prsentation des objets plus complexes que le simple objet [String]. Cela prsente parfois un rel intrt. Prenons l'exemple d'un formulaire avec liste. Celle-ci doit tre prsente au client avec un code html qui ressemble ceci :
<select name="uneListe" size="3"> <option value="opt1">option1</option> <option value="opt2">option2</option> <option value="opt3" selected>option3</option> </select>

Le contenu de la liste et l'option slectionner sont des lments dynamiques et donc doivent tre gnrs par le contrleur de page. Nous avons dj rencontr ce problme et l'avons rsolu en mettant dans le code de prsentation la balise
<%=uneListeHTML%>

Cette balise va tre remplace par la valeur [String] de la variable [uneListeHTML]. Cette valeur calcule par le contrleur devra tre le code HTML de la liste, c.a.d. "<select name=..>...</select>". Ce n'est pas particulirement difficile faire et cela parat une solution lgante qui vite de mettre du code de gnration directement dans la partie prsentation de la page. Ici il y aurait une boucle avec des tests insrer dans celle-ci qui serait ainsi fortement "pollue". Nanmoins, cette mthode prsente un inconvnient. Le dcoupage contrle/prsentation d'une page sert aussi dlimiter deux domaines de comptence : celui du dveloppeur .NET qui s'occupe du contrleur de page celui de l'infographiste qui s'occupe de la partie prsentation de celle-ci Ici, on voit qu'on a transfr la gnration du code HTML de la liste dans le contrleur. L'infographiste peut vouloir agir sur ce code HTML pour modifier l'aspect "visuel" de la liste. Il sera oblig de travailler dans la partie [contrleur] et donc de sortir de son domaine de comptence avec les risques d'erreurs introduites malencontreusement dans le code que cela comporte. Composants serveur - 2 3/192

Les bibliothques de balises serveur rsolvent ce problme. Elles proposent un objet reprsentant une liste HTML. Ainsi la bibliothque [WebForms] offre la balise suivante : <asp:ListBox id="uneListe" runat="server"></asp:ListBox> Cette balise reprsente un objet de type [ListBox] qui peut tre manipul par le contrleur de page. Cet objet a des proprits pour reprsenter les diffrentes options de la liste HTML et dsigner l'option slectionne. Le contrleur de page va donc donner les valeurs adquates ces proprits. Lorsque la partie prsentation va tre excute, la balise <asp:ListBox id="uneListe" runat="server"></asp:ListBox> va tre remplace par le code HTML reprsentant l'objet [uneListe], c.a.d. le code "<select ..>...</select>". Pour l'instant, pas de diffrence fondamentale avec la mthode prcdente sinon une faon de coder oriente objet, ce qui est intressant. Revenons notre infographiste qui a besoin de modifier le "look" de la liste. Les balises serveur ont des attributs de style (BackColor, Bordercolor, BorderWidth, ...) qui permettent de fixer l'aspect visuel de l'objet HTML correspondant. Ainsi on pourra crire :
<asp:ListBox id="ListBox1" runat="server" BackColor="#ffff99"></asp:ListBox></P>

L'intrt est que l'infographiste reste dans le code de prsentation pour faire ces modifications. C'est un avantage certain vis vis de la mthode prcdente. Les bibliothques de balises serveur amnent donc des facilits dans la construction de la partie prsentation des pages, ce qu'on a appel dans les chapitres prcdents, l'interface utilisateur. Ce chapitre a pour but de les prsenter. On verra qu'elle propose parfois des objets complexes tels que des calendriers ou des tables lies des sources de donnes. Elles sont extensibles, c.a.d. que l'utilisateur peut crer sa propre bibliothque de balises. Il peut ainsi raliser une balise gnrant un bandeau dans une page. Toutes les pages utilisant cette balise auront alors le mme bandeau. La gnration HTML faite pour une balise s'adapte au type du navigateur client. Lorsque celui-ci fait une requte au serveur web, il envoie parmi ses enttes HTTP, un entte [User-Agent: xx] o [xx] identifie le client. Voici un exemple :
User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316

Avec cette information, le serveur web peut connatre les capacits du client, notamment le type de code HTML qu'il sait grer. Il y a eu en effet, au fil du temps, plusieurs versions du langage HTML. Les navigateurs rcents comprennent les versions les plus rcentes du langage, ce que ne savent pas faire des navigateurs plus anciens. En fonction de l'entte HTTP [User-Agent:] que le client lui a envoy, le serveur lui enverra une version HTML qu'il saura comprendre. C'est une ide intressante et utile car le dveloppeur n'a alors pas se proccuper du type de navigateur client de son application. Enfin, des IDE volus comme Visual Studio.NET, WebMatrix, ... permettent une conception " la windows" de l'interface Web. Ces outils non indispensables, apportent cependant une aide dcisive au dveloppeur. Celui-ci dessine l'interface web l'aide de composants graphiques qu'ils dposent sur cette interface. Il a un accs direct aux proprits de chacun des composants de l'interface qu'il peut ainsi paramtrer sa guise. Ces proprits seront traduites dans le code HTML de prsentation de l'interface en attributs de la balise <asp:> du composant. L'intrt pour le dveloppeur est qu'il n'a pas se rappeler ni la liste ni la syntaxe des attributs de chaque balise. C'est un avantage apprciable lorsqu'on ne connat pas parfaitement les bibliothques de balises serveur offertes par ASP.NET. Lorsque cette syntaxe est acquise, certains dveloppeurs pourront prfrer coder directement les balises dans le code de prsentation de la page sans passer par la phase conception graphique. Un IDE n'est plus alors utile. Un simple diteur de texte suffit. Selon la faon dont on travaille, l'accent est alors mis sur les composants (utilisation d'un IDE) ou les balises (utilisation d'un diteur de texte). Il y a quivalence entre ces deux termes. Le composant est l'objet qui va tre manipul par le code de contrle de la page. L'IDE nous donne accs ses proprits en phase de conception. Les valeurs donnes celles-ci sont traduites immdiatement dans les attributs de la balise du composant dans le code de prsentation. En phase d'excution, le code de contrle de la page va manipuler le composant et affecter des valeurs certaines de ses proprits. Le code de prsentation va lui gnrer le code HTML du composant en utilisant d'une part les attributs fixs la conception pour la balise serveur correspondante, d'autre part les valeurs des proprits du composant calcules par le code de contrle.

1.2 Le contexte d'excution des exemples


Nous allons illustrer la conception des interfaces web base de composants serveur avec des programmes dont le contexte d'excution sera la plupart du temps le suivant : l'application web sera compose d'une unique page P contenant un formulaire F, le client fera sa premire requte directement cette page P. Cela consistera demander l'url de la page P avec un navigateur. C'est donc une requte GET qui sera faite sur cette url P. Le serveur dlivrera la page P et donc le formulaire F qu'elle contient, 3. l'utilisateur remplira celui-ci et le postera, c.a.d. qu'il fera une action qui forcera le navigateur poster le formulaire F au serveur. L'opration POST du navigateur sera toujours destination de la page P. Le serveur dlivrera de nouveau la page P avec le formulaire F, le contenu de ce dernier ayant pu tre modifi par l'action de l'utilisateur. Composants serveur - 2 4/192 1. 2.

4.

puis les tapes 2 et 3 reprendront.

C'est un processus d'excution bien particulier en-dehors duquel certains concepts exposs ci-aprs ne fonctionnent plus. Nous ne sommes plus dans un contexte d'architecture MVC dans lequel une application multi-pages est contrle par une page particulire que nous avons appele "contrleur d'application". Dans ce type d'architecture, le POST des formulaires ont pour cible le contrleur et non les formulaires eux-mmes. Or nous allons voir que construire un formulaire avec des composants serveur implique que ce formulaire soit post lui-mme.

1.3 Le composant Label


1.3.1 Utilisation
La balise <asp:label> permet d'insrer un texte dynamique dans le code de prsentation d'une page. Ca ne fait donc pas davantage que la balise <%=variable%> utilise jusqu' maintenant. L'tude de cette premire balise va nous permettre de dcouvrir le mcanisme des balises serveur. Nous crons une page avec une partie contrle [form1.aspx.vb] et une partie prsentation [form1.aspx] . Il s'agit d'afficher l'heure :

Ce problme a dj t trait dans le chapitre 2 et le lecteur est invit s'y reporter s'il souhaite savoir comment il avait t trait. Le code de prsentation [form1.aspx] est le suivant :
<%@ page src="form1.aspx.vb" inherits="form1" AutoEventWireup="false" %> <HTML> <HEAD> <title>Webforms</title> </HEAD> <body> <asp:Label Runat="server" ID="lblHeure" /> </body> </HTML>

Nous introduisons la balise <asp:label>. Dans les bibliothque de balises, l'attribut [runat="server"] est obligatoire. L'attribut ID identifie le composant. Le contrleur devra le rfrencer avec cet identifiant. Le code du contrleur [form1.aspx.vb] est le suivant : Imports
System.Web.UI.WebControls

Public Class form1 Inherits System.Web.UI.Page Protected lblHeure As Label Private Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Init ' sauve la requte courante dans request.txt du dossier de la page Dim requestFileName As String = Me.MapPath(Me.TemplateSourceDirectory) + "\request.txt" Me.Request.SaveAs(requestFileName, True) End Sub Private Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load ' met l'heure dans lblHeure lblHeure.Text = "Il est " + Date.Now.ToString("T") End Sub End Class

Le contrleur doit donner une valeur l'objet [lblHeure] de type [System.Web.UI.WebControls.Label]. Tous les objets affichs par les balises <asp:> appartiennent l'espace de noms [System.Web.UI.WebControls]. Aussi pourra-t-on importer systmatiquement cet espace de noms : Imports
System.Web.UI.WebControls

L'objet [Label] a diffrentes proprits dont la proprit [Text] qui reprsente le texte qui sera affich par la balise <asp:label> correspondante. Ici, nous mettons dans cette proprit l'heure courante. Nous le faisons dans la procdure [Form_Load] du Composants serveur - 2 5/192

contrleur qui est systmatiquement excut. Dans la procdure [Form_Init] qui elle aussi est toujours excute mais avant la procdure [Form_Load], nous mmorisons la requte du client dans un fichier [request.txt] du dossier de l'application. Nous aurons l'occasion d'examiner ce fichier pour comprendre certains aspects du fonctionnement des pages utilisant des balises serveur. L'objet [Label] possde de nombreuses proprits, mthodes et vnements. Le lecteur est invit lire la documentation sur la classe [Label] pour les dcouvrir. Dans la suite, il en sera toujours ainsi. Pour chaque balise, nous ne prsentons que les quelques proprits dont nous avons besoin.

1.3.2 Les tests


Nous plaons les fichiers (form1.aspx, form1.aspx.vb) dans un dossier <application-path> et lanons Cassini avec les paramtres (<application-path>,/form1). Puis nous demandons l'url [http://localhost/form1/form1.aspx]. Nous obtenons le rsultat suivant :

Le code HTML reu par le navigateur est le suivant :


<HTML> <HEAD> <title>Webforms</title> </HEAD> <body> <span id="lblHeure">Il est 19:39:37</span> </body> </HTML>

On voit que la balise serveur


<asp:Label Runat="server" ID="lblHeure" />

a t transforme en le code HTML suivant :


<span id="lblHeure">Il est 19:39:37</span>

C'est la proprit [Text] de l'objet [lblHeure] qui a t plac entre les balises <span> et </span>. La requte faite par le client et mmorise dans [request.txt] est la suivante :
GET /form1/form1.aspx HTTP/1.1 Cache-Control: max-age=0 Connection: keep-alive Keep-Alive: 300 Accept: application/x-shockwaveflash,text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg ,image/gif;q=0.2,*/*;q=0.1 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Accept-Encoding: gzip,deflate Accept-Language: en-us,en;q=0.5 Host: localhost User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316

Rien que de trs normal.

1.3.3 Construire l'application avec WebMatrix


Nous avons construit le code de prsentation de la page [form1.aspx] la main. Cette mthode est utilisable si on connat les balises. Un simple diteur de texte suffit alors pour construire l'interface utilisateur. Pour dbuter, un outil de conception graphique alli une gnration de code automatique est souvent ncessaire parce qu'on ne connat pas la syntaxe des balises dont on a besoin. Nous construisons maintenant la mme application avec l'outil WebMatrix. Une fois WebMatrix lanc, nous choisissons l'option [File/New File] :

Composants serveur - 2

6/192

Nous crons une page ASP.NET appele [form2.aspx]. Une fois valid l'assistant prcdent, nous obtenons la fentre de conception de la page [form2.aspx] :

On rappelle que WebMatrix met le code de contrle de la page et celui de prsentation dans un mme fichier, ici [form2.aspx]. L'onglet [All] prsente le contenu de ce fichier texte. On peut constater ds maintenant qu'il n'est pas vide :

L'inconvnient de ce type d'outils est que souvent ils gnrent du code inutile. C'est le cas ici, o WebMatrix a gnr une balise HTML <form> alors qu'on ne va pas construire de formulaire... Par ailleurs, on peut constater que le document n'a pas de balise <title>. Nous remdions ces deux problmes sur le champ pour obtenir la nouvelle version suivante :

Composants serveur - 2

7/192

Ce que nous appelons le code contrleur va venir s'insrer entre les balises <script> et </script>, ce qui assure une sparation au moins visuelle entre les deux types de code : contrle et prsentation. Nous revenons sur l'onglet [Design] pour dessiner notre interface. Une liste de composants est disponible dans une fentre d'outils gauche de la fentre de conception :

La fentre d'outils offre l'accs deux types de composants : les composants [WebControls] qui se traduisent par des balises <asp:> les composants [HTML Elements] qui se traduisent par des balises HTML classiques. Cependant, on peut ajouter aux attributs d'une balise HTML, l'attribut [runat="server"]. Dans ce cas, la balise HTML et ses attributs sont accessibles au contrleur via un objet ayant pour proprits celles de la balise HTML qu'il reprsente. On a appel prcdemment ces balises, les balises HTML serveur. Double-cliquons sur le composant [Label] de la liste de contrles [WebControls]. Dans l'onglet [Design] on obtient le rsultat suivant :

Dans l'onglet [All] le code est devenu le suivant :


<%@ Page Language="VB" %> <html> <head> <title>webforms</title> </head> <body> <asp:Label id="Label1" runat="server">Label</asp:Label> </body> </html>

On peut remarquer tout d'abord qu'on a perdu la balise <script>. Une balise <asp:label> a t gnre. Elle a un nom [Label1] et une valeur [Label]. Revenons sur l'onglet [Design] pour modifier ces deux valeurs. Nous y cliquons une fois sur le composant [Label] pour faire apparatre la fentre de proprits de ce composant, en bas droite :

Composants serveur - 2

8/192

Le lecteur est invit consulter les proprits de l'objet [Label]. Deux nous intressent ici : Text : c'est le texte que doit afficher le label - nous mettons la chane vide (c.a.d. rien) ID : c'est son identifiant - nous mettons lblHeure L'onglet [Design] devient ceci :

et le code de [All] devient :


<%@ Page Language="VB" %> <html> <head> <title>webforms</title> </head> <body> <asp:Label id="lblHeure" runat="server"></asp:Label> </body> </html>

La partie prsentation de la page est finie. Il nous reste crire le code de contrle charg de mettre l'heure dans la proprit [Text] de [lblHeure]. Nous ajoutons dans l'onglet [All] le code suivant :
<%@ Page Language="VB" %> <script runat="server"> Private Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load ' met l'heure dans lblHeure lblHeure.Text = "Il est " + Date.Now.ToString("T") End Sub </script> <html> <head> <title>webforms</title> </head> <body> <asp:Label id="lblHeure" runat="server"></asp:Label> </body> </html>

On remarquera que dans le code contrleur, on ne dclare pas l'objet [lblHeure] comme il avait t fait prcdemment :
Protected lblHeure As New System.Web.UI.WebControls.Label

En effet, tous les composants serveur <asp:> de la partie prsentation font l'objet d'une dclaration implicite dans la partie code de contrle. Aussi les dclarer provoque une erreur de compilation, celle-ci indiquant que l'objet est dj dclar. Nous sommes prts pour l'excution. Nous prenons l'option [View/Start] ou le raccourci [F5]. Cassini est automatiquement lanc avec les paramtres suivants : Composants serveur - 2 9/192

Nous acceptons ces valeurs. Le navigateur par dfaut du systme est automatiquement lanc pour demander l'url [http://localhost/form2.aspx]. Nous obtenons le rsultat suivant :

Par la suite, nous utiliserons principalement l'outil WebMatrix afin de faciliter la construction et les tests des courts programmes que nous crirons.

1.4 Le composant Literal


1.4.1 Utilisation
La balise <asp:literal> permet d'insrer un texte dynamique dans le code de prsentation d'une page comme la balise <asp:label>. Son principal attribut est [Text] qui reprsente le texte qui sera insr tel quel dans le flux HTML de la page. Cette balise est suffisante si on n'a pas l'intention de mettre en forme le texte que l'on veut insrer dans le flux HTML. En effet si la classe [Label] permet de mettre en forme grce des attributs tels que [BorderColor, BorderWidth, Font, ...], la classe [Literal] n'a aucun de ces attributs. Le lecteur pourra reprendre intgralement l'exemple prcdent en remplaant le composant [Label] par un composant [Literal].

1.5 Le composant Button


1.5.1 Utilisation
La balise <asp:Button> permet d'insrer un bouton de type [Submit] dans un formulaire qui amne avec lui une gestion d'vnements semblable celle que l'on trouve dans les applications windows. C'est ce point qu'on veut approfondir ici. Nous crons la page [form3.aspx] suivante : 1 2

3 Cette page construite avec WebMatrix a trois composants : n 1 2 3 nom


Button1 Button2 lblInfo

type Button Button Label

proprits text=Bouton1 text=Bouton2 text=

rle bouton submit bouton submit message d'information

Le code gnr par WebMatrix pour cette partie est le suivant :


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

Composants serveur - 2

10/192

</script> <html> <head> <title>asp:button</title> </head> <body> <form runat="server"> <p> <asp:Button id="Button1" runat="server" Text="Bouton1"></asp:Button> <asp:Button id="Button2" runat="server" Text="Bouton2"></asp:Button> </p> <p> <asp:Label id="lblInfo" runat="server"></asp:Label> </p> </form> </body> </html>

Nous retrouvons les composants [Button] et [Label] utiliss lors de la conception graphique de la page dans des balises <asp:>. Notons la balise <form runat="server"> qui a t gnre automatiquement. C'est une balise HTML serveur, c.a.d. une balise HTML classique reprsente nanmoins par un objet manipulable par le contrleur. Le code HTML de la balise <form> sera gnr partir de la valeur que le contrleur donnera cet objet. Ajoutons dans la partie contrleur du code, la procdure [Page_Init] qui gre l'vnement [Init] de la page. Nous y plaons le code qui sauvegarde la requte du client dans le fichier [request.txt]. Nous en aurons besoin pour comprendre le mcanisme des boutons.
<script runat="server"> Private Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) ' sauve la requte courante dans request.txt du dossier de la page Dim requestFileName As String = Me.MapPath(Me.TemplateSourceDirectory) + "\request.txt" Me.Request.SaveAs(requestFileName, True) End Sub </script>

On notera ci-dessus que nous n'avons pas mis la clause [Handles MyBase.Init] derrire la dclaration de la procdure [Page_Init]. En effet, l'vnement [Init] de l'objet [Page] a un gestionnaire par dfaut qui s'appelle [Page_Init]. Si on utilise ce nom de gestionnaire, la clause [Handles Page.Init] devient inutile. La mettre ne provoque cependant pas d'erreur.

1.5.2 Tests
Nous lanons l'application sous WebMatrix par [F5]. Nous obtenons la page suivante :

Le code HTML reu par le navigateur est le suivant :


<html> <head> <title>asp:button</title> </head> <body> <form name="_ctl0" method="post" action="form3.aspx" id="_ctl0"> <input type="hidden" name="__VIEWSTATE" value="dDwxNTY0NjIwMjUwOzs+2mcnJczeuvF2PEfvmtv7uiUhWUw=" /> <p> <input type="submit" name="Button1" value="Bouton1" id="Button1" /> <input type="submit" name="Button2" value="Bouton2" id="Button2" /> </p> <p> <span id="lblInfo"></span> </p> </form> </body> </html>

Composants serveur - 2

11/192

Notons les points suivants : la balise <form runat="server"> s'est transforme en balise HTML
<form name="_ctl0" method="post" action="form3.aspx" id="_ctl0">

Deux attributs ont t fixs [method="post"] et [action="form3.aspx"]. Peut-on donner des valeurs diffrentes ces attributs ? Nous essaierons d'claircir ce point un peu plus loin. On retiendra ici que le formulaire sera post l'url [form3.aspx]. Deux autres attributs [name, id] ont t galement fixs. La plupart du temps, ils sont ignors. Cependant si la page contient du code Javascript excut ct navigateur, l'attribut [name] de la balise <form> est utile. les balises <asp:button> sont devenues des balises HTML de boutons [submit]. Un clic sur l'un quelconque de ces boutons provoquera donc un "post" du formulaire [_ctl10] l'url [form3.aspx]. la balise <asp:label> est devenue une balise HTML <span> un champ cach [__VIEWSTATE] a t gnr avec une valeur bizarre :
<input /> type="hidden" name="__VIEWSTATE" value="dDwxNTY0NjIwMjUwOzs+2mcnJczeuvF2PEfvmtv7uiUhWUw="

Ce champ reprsente sous une forme code l'tat du formulaire envoy au client. Cet tat reprsente la valeur de tous les composants de celui-ci. Comme [__VIEWSTATE] fait partie du formulaire, sa valeur sera poste avec le reste au serveur. Cela permettra celui-ci de savoir quel composant du formulaire a chang de valeur et ventuellement de prendre des dcisions. Celles-ci prendront la forme d'vnements du type "le TextBox untel a chang de valeur".

1.5.3 Les requtes du client


Une fois obtenue la page [form3.aspx] dans le navigateur, redemandons-la puis regardons la requte qui a t envoye par le navigateur pour l'obtenir. On se rappelle que notre application la mmorise dans le fichier [request.txt] dans le dossier de l'application :
GET /form3.aspx HTTP/1.1 Connection: keep-alive Keep-Alive: 300 Accept: application/x-shockwaveflash,text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg ,image/gif;q=0.2,*/*;q=0.1 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Accept-Encoding: gzip,deflate Accept-Language: en-us,en;q=0.5 Host: localhost User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316

On a l un GET classique. Maintenons cliquons sur le bouton [Bouton1] de la page dans le navigateur. Il ne se passe apparemment rien. Pourtant nous savons que le formulaire a t post. C'est le code HTML de la page qui le dit. Cela est confirm par le nouveau contenu de [request.txt] :
POST /form3.aspx HTTP/1.1 Connection: keep-alive Keep-Alive: 300 Content-Length: 80 Content-Type: application/x-www-form-urlencoded Accept: application/x-shockwaveflash,text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg ,image/gif;q=0.2,*/*;q=0.1 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Accept-Encoding: gzip,deflate Accept-Language: en-us,en;q=0.5 Host: localhost Referer: http://localhost/form3.aspx User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316 __VIEWSTATE=dDwxNTY0NjIwMjUwOzs%2B2mcnJczeuvF2PEfvmtv7uiUhWUw%3D&Button1=Bouton1

Le premier entte HTTP indique bien que le client a fait un POST vers l'url [/form3.aspx]. La dernire ligne montre les valeurs postes : la valeur du champ cach __VIEWSTATE la valeur du bouton qui a t cliqu Composants serveur - 2 12/192

Si nous cliquons sur [Bouton2], les valeurs postes par le navigateur sont les suivantes :
__VIEWSTATE=dDwxNTY0NjIwMjUwOzs%2B2mcnJczeuvF2PEfvmtv7uiUhWUw%3D&Button2=Bouton2

La valeur du champ cach est toujours la mme mais c'est la valeur de [Button2] qui a t poste. Le serveur peut donc savoir quel bouton a t utilis. Il va s'en servir pour dclencher un vnement qui pourra tre trait par la page, une fois que celle-ci aura t charge.

1.5.4 Grer l'vnement Click d'un objet Button


Rappelons le fonctionnement d'une page .aspx. Celle-ci est un objet driv de la classe [Page]. Appelons la classe drive [unePage]. Lorsque le serveur reoit une requte pour une telle page, un objet de type [unePage] est instanci par une opration new unePage(...). Puis ensuite, le serveur gnre deux vnements appels [Init] et [Load] dans cet ordre. L'objet [unePage] peut les grer en fournissant les gestionnaires d'vnements [Page_Init] et [Page_Load]. Ensuite d'autres vnements seront gnrs. Nous aurons l'occasion d'y revenir. Si la requte du client est un POST, le serveur gnrera l'vnement [Click] du bouton qui a provoqu ce POST. Si la classe [unePage] a prvu un gestionnaire pour cet vnement, celui-ci sera appel. Voyons ce mcanisme avec WebMatrix. Dans l'onglet [Design] de [form3.aspx], double-cliquons sur le bouton [Bouton1]. Nous sommes alors amens automatiquement dans l'onglet [Code], dans le corps d'une procdure appele [Button1_Click]. Pour mieux comprendre, allons dans l'onglet [All] et regardons la totalit du code. Les modifications suivantes ont t apportes :
<%@ Page Language="VB" %> <script runat="server"> ... Sub Button1_Click(sender As Object, e As EventArgs) End Sub </script> <html> ... <body> <form runat="server"> ... <asp:Button id="Button1" onclick="Button1_Click" runat="server" Text="Bouton1"></asp:Button> </form> </body> </html>

Un nouvel attribut [onclick="Button1_Click"] a t ajout la balise <asp:Button> de [Button1]. Cet attribut indique la procdure charge de traiter l'vnement [Click] sur l'objet [Button1], ici la procdure [Button1_Click]. Il ne nous reste plus qu' crire celle-ci :
Sub Button1_Click(sender As Object, e As EventArgs) ' clic sur bouton 1 lblInfo.Text="Vous avez cliqu sur [Bouton1]" End Sub

La procdure met dans le label [lblInfo] un message d'information. Nous procdons de la mme faon pour le bouton [Bouton2] pour obtenir la nouvelle page [form3.aspx] suivante :
<%@ Page Language="VB" %> <script runat="server"> Private Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) ' sauve la requte courante dans request.txt du dossier de la page Dim requestFileName As String = Me.MapPath(Me.TemplateSourceDirectory) + "\request.txt" Me.Request.SaveAs(requestFileName, True) End Sub Sub Button1_Click(sender As Object, e As EventArgs) ' clic sur bouton 1 lblInfo.Text="Vous avez cliqu sur [Bouton1]" End Sub Sub Button2_Click(sender As Object, e As EventArgs) ' clic sur bouton 2 lblInfo.Text="Vous avez cliqu sur [Bouton2]" End Sub </script> <html>

Composants serveur - 2

13/192

<head> <title>asp:button</title> </head> <body> <form runat="server"> <p> <asp:Button id="Button1" onclick="Button1_Click" runat="server" Text="Bouton1"></asp:Button> <asp:Button id="Button2" onclick="Button2_Click" runat="server" Text="Bouton2" BorderStyle="None"></asp:Button> </p> <p> <asp:Label id="lblInfo" runat="server"></asp:Label> </p> </form> </body> </html>

Nous lanons l'excution par [F5] pour obtenir la page suivante :

Si nous regardons le code HTML reu, nous constaterons qu'il n'a pas chang vis vis de celui de la version prcdente de la page :
<html> <head> <title>asp:button</title> </head> <body> <form name="_ctl0" method="post" action="form3.aspx" id="_ctl0"> <input type="hidden" name="__VIEWSTATE" value="dDwxNTY0NjIwMjUwOzs+2mcnJczeuvF2PEfvmtv7uiUhWUw=" /> <p> <input type="submit" name="Button1" value="Bouton1" id="Button1" /> <input type="submit" name="Button2" value="Bouton2" id="Button2" /> </p> <p> <span id="lblInfo"></span> </p> </form> </body> </html>

Si nous cliquons sur [Bouton1], nous obtenons la rponse suivante :

Le code HTML reu pour cette rponse est le suivant :


<html> <head> <title>asp:button</title> </head> <body> <form name="_ctl0" method="post" action="form3.aspx" id="_ctl0">

Composants serveur - 2

14/192

<input type="hidden" name="__VIEWSTATE" value="dDwxNTY0NjIwMjUwO3Q8O2w8aTwxPjs+O2w8dDw7bDxpPDU+Oz47bDx0PHA8cDxsPFRleHQ7PjtsPFZvdXMgYXZleiBjbGlxdcO pIHN1ciBbQm91dG9uMV07Pj47Pjs7Pjs+Pjs+Pjs+4oO98Vd244kj0lPMXReWOwJ1WW0=" /> <p> <input type="submit" name="Button1" value="Bouton1" id="Button1" /> <input type="submit" name="Button2" value="Bouton2" id="Button2" /> </p> <p> <span id="lblInfo">Vous avez cliqu sur [Bouton1]</span> </p> </form> </body> </html>

Nous pouvons constater que le champ cach [__VIEWSTATE] a chang de valeur. Cela reflte le changement de valeur du composant [lblInfo].

1.5.5 Les vnements de la vie d'une application ASP.NET


La documentation ASP.NET donne la liste des vnements gnrs par le serveur au cours de la vie d'une application : lors de la toute premire requte que reoit l'application, l'vnement [Start] sur l'objet [HttpApplication] de l'application va tre gnr. Cet vnement peut tre gr par la procdure [Application_Start] du fichier [global.asax] de l'application.

Ensuite nous allons avoir une srie d'vnements qui vont se rpter pour chaque requte reue : si la requte n'a pas envoy de jeton de session, une nouvelle session est dmarre et un vnement [Start] sur l'objet [Session] associ la requte est gnr. Cet vnement peut tre gr par la procdure [Session_Start] du fichier [global.asax] de l'application. le serveur gnre l'vnement [BeginRequest] sur l'objet [HttpApplication]. Il peut tre gr par la procdure [Application_BeginRequest] du fichier [global.asax] de l'application. le serveur charge la page demande par la requte. Il va instancier un objet [Page] puis gnrer deux vnements sur cet objet : [Init] puis [Load]. Ces deux vnements peuvent tre grs par les procdure [Page_Init] et [Page_Load] de la page. partir des valeurs postes reues, le serveur va gnrer d'autres vnements : [TextChanged] pour un composant [TextBox] qui a chang de valeur, [CheckedChanged] pour un bouton radio qui a chang de valeur, [SelectedIndexChanged] pour une liste qui a chang d'lment slectionn, ... Nous aurons l'occasion de mentionner les principaux vnements pour chacun des composants serveur que nous allons prsenter. Chaque vnement E sur un objet nomm O peut tre gr par une procdure portant le nom O_E. l'ordre de gestion des vnements prcdents n'est pas garanti. Aussi les gestionnaires ne doivent-ils faire aucune hypothse sur cet ordre. On est cependant assur que l'vnement [Click] du bouton qui a provoqu le POST est trait en dernier. une fois la page prte, le serveur va l'envoyer au client. Avant, il gnre l'vnement [PreRender] qui peut tre gr par la procdure [Page_PreRender] de la page. une fois la rponse HTML envoye au client, la page va tre dcharge de la mmoire. Deux vnements seront gnrs cette occasion [Unload] et [Disposed]. La page peut utiliser ces vnements pour librer des ressources.

L'application peut galement recevoir des vnements en-dehors d'une requte client : l'vnement [End] sur un objet [Session] de l'application se produit lorsqu'une session se termine. Cela peut se produire sur une demande explicite du code d'une page soit parce que la dure de vie accorde la session est dpass. La procdure [Session_End] du fichier [global.asax] gre cet vnement. On y libre en gnral des ressources obtenues dans [Session_Start]. l'vnement [End] sur l'objet [HttpApplication] de l'application se produit lorsque l'application se termine. Cela se produit notamment lorsqu'on arrte le serveur Web. La procdure [Application_End] du fichier [global.asax] gre cet vnement. On y libre en gnral des ressources obtenues dans [Application_Start].

On retiendra les points suivants : le modle vnementiel prcdent s'appuie sur des changes HTTP client-serveur classiques. On le voit parfaitement lorsqu'on examine les enttes HTTP changs. le traitement des vnements prcdents se fait toujours ct serveur. Le clic sur un bouton peut bien sr tre trait par un script Javascript ct serveur. Mais il ne s'agit pas alors d'un vnement serveur et on est l dans une technologie indpendante de ASP.NET. A quel moment sont traits les vnements, qu'ils soient traits du ct serveur (vnements lis aux composants serveur) ou du ct navigateur par des scripts Javascript ? Composants serveur - 2 15/192

Prenons l'exemple d'une liste droulante. Lorsque l'utilisateur change l'lment slectionn dans celle-ci, l'vnement (changement de l'lment slectionn) peut tre trait ou non et s'il est trait il peut l'tre diffrents moments. si on souhaite le traiter immdiatement, on a deux solutions : o il peut tre trait par le navigateur l'aide d'un script Javascript. Le serveur n'intervient alors pas. Pour que cela soit possible, il faut que la page puisse tre reconstruite avec des valeurs prsentes dans la page. o il peut tre trait par le serveur. Pour cela, il n'y a qu'une solution : le formulaire doit tre envoy au serveur pour traitement. On a donc une opration [submit]. On verra que dans ce cas, on utilise un composant serveur appel [DropDownList] et on fixe son attribut [AutoPostBack] [true]. Cela veut dire qu'en cas de changement d'lment slectionn dans la liste droulante, le formulaire doit tre immdiatement post au serveur. Le serveur gnre dans ce cas, pour l'objet [DropDownList] un code HTML associ une fonction Javascript charge de faire un [submit] ds que l'vnement "changement de l'lment slectionn" se produit. Ce [submit] postera le formulaire au serveur et dans celui-ci des champs cachs seront positionns pour indiquer que le [post] provient d'un changement de slection dans la liste droulante. Le serveur gnrera alors l'vnement [SelectedIndexChanged] que la page pourra grer. o si on souhaite le traiter mais pas immdiatement, on fixe l'attribut [AutoPostBack] du composant serveur [DropDownList] [false]. Le serveur gnre dans ce cas, pour l'objet [DropDownList] le code HTML classique d'une liste <select> sans fonction Javascript associe. Rien ne se passe alors lorsque l'utilisateur change la slection de la liste droulante. Cependant, lorsqu'il va valider le formulaire par un bouton [submit] par exemple, le serveur va pouvoir reconnatre qu'il y a eu un changement de slection. Nous avons en effet vu que le formulaire envoy au serveur avait un champ cach [__VIEWSTATE] reprsentant sous une forme code l'tat de tous les lments du formulaire envoy. Lorsque le serveur reoit le nouveau formulaire post par le client, il va pouvoir vrifier si la liste droulante a chang ou non d'lment slectionn. Si oui, il va gnrer l'vnement [SelectedIndexChanged] que la page pourra alors grer. Pour distinguer ce mcanisme du prcdent, certains auteurs disent que l'vnement "changement de slection" a t mis "en cache" lorsqu'il s'est produit sur le navigateur . Il ne sera trait par le serveur que lorsque le navigateur lui postera le formulaire, la suite souvent d'un clic sur un bouton [submit]. o enfin si on ne souhaite pas traiter l'vnement, on fixe l'attribut [AutoPostBack] du composant serveur [DropDownList] [false] et on n'crit pas le gestionnaire de son vnement [SelectedIndexChanged]. Une fois compris le mcanisme de traitement des vnements, le dveloppeur ne concevra pas une application web comme une application windows. En effet, si un changement de slection dans un combobox d'une application windows peut tre utilis pour changer immdiatement l'aspect du formulaire dans lequel se trouve celui-ci, on hsitera davantage traiter cet vnement immdiatement dans une application web s'il implique un "post" du formulaire au serveur donc un aller-retour rseau entre le client et le serveur. C'est pourquoi, la proprit [AutoPostBack] des composants serveur est mise [false] par dfaut. Par ailleurs, le mcanisme [AutoPostBack] qui s'appuie sur des scripts javascript gnrs automatiquement par le serveur web dans le formulaire envoy au client ne peut tre utilis que si on est sr que le navigateur client a autoris l'excution des scripts javascript sur son navigateur. Les formulaires sont donc souvent construits de la faon suivante : les composants serveur du formulaire ont leur proprit [AutoPostBack] [false] le formulaire a un ou des boutons chargs de faire le [POST] du formulaire on crit dans le code contrleur de la page, les gestionnaires des seuls vnements qu'on veut grer, le plus souvent l'vnement [Click] sur un des boutons.

1.6 Le composant TextBox


1.6.1 Utilisation
La balise <asp:TextBox> permet d'insrer un champ de saisie dans le code de prsentation d'une page. Nous crons une page [form4.aspx] pour obtenir la prsentation suivante : 1 2

3 4 Cette page construite avec WebMatrix a les composants suivants : n nom type proprits rle 16/192

Composants serveur - 2

1 2 3 3

TextBox1 TextBox2 lblInfo1 lblInfo2

TextBox AutoPostback=true Text= TextBox AutoPostback=false Text= Label text= Label text=

champ de saisie champ de saisie message d'information sur le contenu de [TextBox1] message d'information sur le contenu de [TextBox2]

Le code gnr par WebMatrix pour cette partie est le suivant :


<%@ Page Language="VB" %> <script runat="server"> </script> <html> <head> <title>asp:textbox</title> </head> <body> <form runat="server"> <p> Texte 1 : <asp:TextBox id="TextBox1" runat="server" AutoPostBack="True"></asp:TextBox> </p> <p> Texte 2 : <asp:TextBox id="TextBox2" runat="server"></asp:TextBox> </p> <p> <asp:Label id="lblInfo1" runat="server"></asp:Label> </p> <p> <asp:Label id="lblInfo2" runat="server"></asp:Label> </p> </form> </body> </html>

Das l'onglet [Design], double-cliquons sur le composant [TextBox1]. Le squelette du gestionnaire de l'vnement [TextChanged] de cet objet est alors gnr (onglet [All]) :
<%@ Page Language="VB" %> <script runat="server"> Sub TextBox1_TextChanged(sender As Object, e As EventArgs) End Sub </script> <html> ... <body> ... <asp:TextBox id="TextBox1" runat="server" AutoPostBack="True" OnTextChanged="TextBox1_TextChanged"></asp:TextBox> </p> .... </form> </body> </html>

L'attribut [OnTextChanged="TextBox1_TextChanged"] a t ajout la balise <asp:TextBox id="TextBox1"> pour dsigner le gestionnaire de l'vnement [TextChanged] sur [TextBox1]. C'est la procdure [TextBox1_Changed] que nous crivons maintenant.
Sub TextBox1_TextChanged(sender As Object, e As EventArgs) ' changement de texte lblInfo1.text=Date.now.Tostring("T") + ": evt [TextChanged] 1=["+textbox1.Text+"]" End Sub

sur

[TextBox1].

Texte

Dans la procdure, nous crivons dans le label [lblInfo1] un message signalant l'vnement et indiquant le contenu de [TextBox1]. On fait de mme pour [TextBox2]. Nous indiquons galement l'heure pour mieux suivre le traitement des vnements. Le code final de [form4.aspx] est le suivant :
<%@ Page Language="VB" %> <script runat="server"> Private Sub Page_Init(ByVal sender As Object, ByVal e As System.EventArgs) ' sauve la requte courante dans request.txt du dossier de la page Dim requestFileName As String = Me.MapPath(Me.TemplateSourceDirectory) + "\request.txt"

Composants serveur - 2

17/192

Me.Request.SaveAs(requestFileName, True) End Sub Sub TextBox1_TextChanged(sender As Object, e As EventArgs) ' changement de texte lblInfo1.text=Date.now.Tostring("T") + ": evt [TextChanged] sur [TextBox1]. Texte 1=["+textbox1.Text+"]" End Sub Sub TextBox2_TextChanged(sender As Object, e As EventArgs) ' changement de texte lblInfo2.text=Date.now.Tostring("T") + ": evt [TextChanged] sur [TextBox2]. Texte 2=["+textbox2.Text+"]" End Sub </script> <html> <head> <title>asp:textbox</title> </head> <body> <form runat="server"> <p> Texte 1 : <asp:TextBox id="TextBox1" runat="server" AutoPostBack="True" OnTextChanged="TextBox1_TextChanged"></asp:TextBox> </p> <p> Texte 2 : <asp:TextBox id="TextBox2" runat="server" OnTextChanged="TextBox2_TextChanged"></asp:TextBox> </p> <p> <asp:Label id="lblInfo1" runat="server"></asp:Label> </p> <p> <asp:Label id="lblInfo2" runat="server"></asp:Label> </p> </form> </body> </html>

Nous avons ajout la procdure [Page_Init] pour mmoriser la requte du client comme dans l'exemple prcdent.

1.6.2 Tests
Nous lanons l'application sous WebMatrix par [F5]. Nous obtenons la page suivante :

Le code HTML reu par le navigateur est le suivant :


<html> <head> <title>asp:textbox</title> </head> <body> <form name="_ctl0" method="post" action="form4.aspx" id="_ctl0"> <input type="hidden" name="__EVENTTARGET" value="" /> <input type="hidden" name="__EVENTARGUMENT" value="" /> <input type="hidden" name="__VIEWSTATE" value="dDwtMTY4MDc0MTUxOTs7PoqpeSYSCX7lCiWZvw5p7u+/OrTD" /> <script language="javascript"> <!-function __doPostBack(eventTarget, eventArgument) { var theform = document._ctl0; theform.__EVENTTARGET.value = eventTarget;

Composants serveur - 2

18/192

theform.__EVENTARGUMENT.value = eventArgument; theform.submit(); } // --> </script> <p> Texte 1 : <input name="TextBox1" type="text" id="TextBox1" language="javascript" /> </p> <p> Texte 2 : <input name="TextBox2" type="text" id="TextBox2" /> </p> <p> <span id="lblInfo1"></span> </p> <p> <span id="lblInfo2"></span> </p> </form> </body> </html> onchange="__doPostBack('TextBox1','')"

Il y a beaucoup de choses dans ce code qui ont gnres automatiquement par le serveur. Notons les points suivants : il y a trois champs cachs : [__VIEWSTATE] que nous avons dj rencontr, [__EventTarget] et [__EventArgument]. Ces deux derniers champs sont utiliss pour grer l'vnement navigateur "change" sur le champ de saisie [TextBox1] les balises serveur <asp:textbox> ont donn naissance aux balises HTML <input type="text" ...> qui correspondent aux champs de saisie la balise serveur <asp:textbox id="TextBox1" AutoPostBack="true" ...> a donn naissance une balise <input type="text" ...> ayant un attribut [onchange="__doPostBack('TextBox1','')"]. Cet attribut dit qu'en cas de changement du contenu de [TextBox1], la fonction Javascript [_doPostBack(...)] doit tre excute. Celle-ci est la suivante :
<script language="javascript"> <!-function __doPostBack(eventTarget, eventArgument) { var theform = document._ctl0; theform.__EVENTTARGET.value = eventTarget; theform.__EVENTARGUMENT.value = eventArgument; theform.submit(); } // --> </script>

Que fait la fonction ci-dessus ? Elle affecte une valeur chacun des deux champs cachs [__EventTarget] et [__EventArgument] puis poste le formulaire. Celui-ci est donc envoy au serveur. C'est l'effet [AutoPostBack]. L'vnement navigateur "change" provoque l'excution du code "__doPostBack('TextBox1','')". On en dduit que dans le formulaire post, le champ cach [__EventTarget] aura la valeur 'TextBox1' et le champ cach [__EventArgument] la valeur ''. Cela permettra au serveur de connatre le composant qui a provoqu le POST. la balise serveur <asp:textbox id="TextBox2"...> a donn naissance une balise <input type="text" ...> classique parce que son attribut [AutoPostBack] n'tait pas positionn [true]. la balise <form> indique que le formulaire sera post [form4.aspx] :
<form name="_ctl0" method="post" action="form4.aspx" id="_ctl0">

Faisons notre permier test. Tapons un texte dans le premier champ de saisie :

puis mettons le curseur dans le second champ de saisie. Aussitt, nous obtenons une nouvelle page : Composants serveur - 2

19/192

Que s'est-il pass ? Lorsque le curseur a quitt le premier champ de saisie, le navigateur a vrifi si le contenu de celui-ci avait chang. C'tait le cas. Il a donc gnr ct navigateur, l'vnement [change] sur le champ HTML [TextBox1]. Nous avons vu alors qu'une fonction Javascript s'excutait et postait le formulaire [form4.aspx]. Cette page a donc t recharge par le serveur. Les valeurs postes par le formulaire ont permis au serveur de savoir son tour que le contenu de la balise serveur [TextBox1] avait chang. La procdure [TextBox1_Changed] a donc t excute ct serveur. Elle a plac un message dans le label [lblInfo1]. Une fois cette procdure termine, [form4.aspx] a t envoye au navigateur. C'est pourquoi nous avons maintenant un texte dans [lblInfo1]. Ceci dit, on peut s'tonner d'avoir quelque chose dans le champ de saisie [TextBox1]. En effet, aucune procdure excute ct serveur n'affecte de valeur ce champ. On a l un mcanisme gnral des formulaires web ASP.NET : le serveur renvoie le formulaire dans l'tat o il l'a reu. Pour cela, il raffecte aux composants la valeur qui a t poste pour eux par le client. Pour certains composants, le client ne poste aucune valeur. C'est le cas par exemples des composants <asp:label> qui sont traduits en balises HTML <span>. On se souvient que le formulaire a un champ cach [__VIEWSTATE] qui reprsente l'tat du formulaire lorsqu'il est envoy au client. Cet tat est la somme des tats de tous les composants du formulaire y compris les composants <asp:label> s'il y en a. Comme le champ cach [__VIEWSTATE] est post par le navigateur client, le serveur est capable de restituer l'tat prcdent de tous les composants du formulaire. Il ne lui reste plus qu' modifier ceux qui ont vu leur valeur change par le POST. Regardons maintenant dans [request.txt] la requte qui a t faite par le navigateur :
POST /form4.aspx HTTP/1.1 Connection: keep-alive Keep-Alive: 300 Content-Length: 137 Content-Type: application/x-www-form-urlencoded Accept: application/x-shockwaveflash,text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,image/jpeg ,image/gif;q=0.2,*/*;q=0.1 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Accept-Encoding: gzip,deflate Accept-Language: en-us,en;q=0.5 Host: localhost Referer: http://localhost/form4.aspx User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316 __EVENTTARGET=TextBox1&__EVENTARGUMENT=&__VIEWSTATE=dDwtMTY4MDc0MTUxOTs7PoqpeSYSCX7lCiWZvw5p7u%2B%2FOrTD&T extBox1=premier+texte&TextBox2=

On voit parfaitement le POST ainsi que les paramtres posts. Revenons notre navigateur et saisissons un texte dans le second champ de saisie :

Composants serveur - 2

20/192

Revenons sur le champ de saisie n 1 pour saisir un nouveau texte : rien ne se passe cette fois-ci. Pourquoi ? parce que le champ de saisie [TextBox2] n'ayant pas sa proprit [AutoPostBack] [true], la balise <input type="text"...> gnre pour lui ne gre pas l'vnement [Change] comme le montre son code HTML :
<input name="TextBox2" type="text" id="TextBox2" />

Aucun vnement ne se produit donc lorsqu'on quitte le champ de saisie n 2. Maintenant entrons un nouveau texte dans le champ n 1 :

Quittons le champ de saisie n 1. Aussitt l'vnement [Change] sur ce champ est dtect et le formulaire post au serveur qui renvoie en retour la page suivante :

Que s'est-il pass ? Tout d'abord, le navigateur a post le formulaire. On retrouve ce fait dans la requte du client mmorise dans [request.txt] :
POST /form4.aspx HTTP/1.1 .... User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316 __EVENTTARGET=TextBox1&__EVENTARGUMENT=&__VIEWSTATE=dDwtMTY4MDc0MTUxOTt0PDtsPGk8MT47PjtsPHQ8O2w8aTwxPjtpPD U%2BOz47bDx0PHA8cDxsPFRleHQ7PjtsPHByZW1pZXIgdGV4dGU7Pj47Pjs7Pjt0PHA8cDxsPFRleHQ7PjtsPDE4OjQyOjI5OiBldnQgW1 RleHRDaGFuZ2VkXSBzdXIgW1RleHRCb3gxXS4gVGV4dGUgMT1bcHJlbWllciB0ZXh0ZV07Pj47Pjs7Pjs%2BPjs%2BPjs%2BxLOermpUUU z5rTAa%2FFsjda6lVmo%3D&TextBox1=troisi%C3%A8me+texte&TextBox2=second+texte

Composants serveur - 2

21/192

Le serveur commence par restituer aux composants leurs valeurs prcdentes grce au champ cach [__VIEWSTATE] que le client lui a envoy. Grce aux champs posts [TextBox1] et [TextBox2] il va affecter aux composants [TextBox1] et [TextBox2] les valeurs qui lui ont t postes. C'est par ce mcanisme que le client va retrouver le formulaire tel qu'il a t post. Puis, toujours grce aux champs posts [__VIEWSTATE], [TextBox1] et [TextBox2], le serveur va dcouvrir que les valeurs des champs de saisie [TextBox1] et [TextBox2] ont chang. Il va donc gnrer les vnements [TextChanged] pour ces deux objets. Le procdures [TextBox1_TextChanged] et [TextBox2_TextChanged] vont tre excutes et les labels [labelInfo1] et [labelInfo2] recevoir une nouvelle valeur. Puis la page [form4.aspx] ainsi modifie est renvoye au client. Maintenant nous modifions de nouveau le champ de saisie n 1 :

Lorsque nous sortons le curseur de saisie du champ 1, l'vnement [Change] se produit sur le navigateur. Ensuite la squence d'vnements dj explique (post du navigateur vers le serveur, ..., envoi de la rponse du serveur) se droule. On obtient la rponse suivante :

A cause de l'heure affiche pour chacun des messages, on voit que seule la procdure [TextBox1_Changed] a t excute sur le serveur. La procdure [TextBox2_TextChanged] n'a pas t excute parce que justement la valeur de [TextBox2] n'a pas chang. Enfin, mettons un nouveau texte dans le champ n 2 :

Composants serveur - 2

22/192

Puis plaons le curseur sur le champ n 1 puis remettons-le sur le champ n 2. La page ne change pas. Pourquoi ? Parce que nous ne changeons pas la valeur du champ n 1, l'vnement navigateur [Change] ne se produit pas lorsque nous quittons ce champ. Du coup, le formulaire n'est pas post au serveur. Donc rien ne change sur la page. C'est le fait que le contenu de [lblInfo2] ne change pas qui nous montre qu'il n'y a pas de POST. S'il y en avait un, le serveur dtecterait que le contenu de [TextBox2] a chang et devrait reflter ce fait dans [lblInfo2]. On retiendra de cet exemple qu'il n'y a pas d'intrt mettre la proprit [AutoPostBack] d'un [TextBox] [true]. Cela provoque un aller-retour client-serveur inutile la majeure partie du temps.

1.6.3 Le rle du champ __VIEWSTATE


Nous avons vu que le serveur mettait systmatiquement un champ cach appel __VIEWSTATE dans le formulaire qu'il gnrait. Nous avons dit que ce champ reprsentait l'tat du formulaire et que si on lui retournait ce champ cach, le serveur tait capable de reconstituer la valeur prcdente du formulaire. L'tat d'un formulaire est la somme des tats de ses composants. Chacun d'eux a une proprit [EnableViewState] valeur boolenne indiquant si l'tat du composant doit tre plac ou non dans le champ cach [__VIEWSATE]. Par dfaut cette proprit a la valeur [true] faisant que l'tat de tous les composants d'un formulaire est plac dans [__VIEWSTATE]. Quelquefois, ce n'est pas souhaitable. Faisons quelques expriences pour mieux comprendre le rle de la proprit [EnableViewState]. Mettons cette proprit [false] pour les deux champs de saisie :
... <asp:TextBox id="TextBox1" runat="server" OnTextChanged="TextBox1_TextChanged" AutoPostBack="True" EnableViewState="False"></asp:TextBox> ... <asp:TextBox id="TextBox2" runat="server" OnTextChanged="TextBox2_TextChanged" EnableViewState="False"></asp:TextBox> ...

Excutons maintenant l'application et tapons un premier texte dans le champ n 1 avant de passer dans le champ n 2. Un POST est alors fait vers le serveur et on obtient la rponse suivante :

Tapons un texte dans le champ n 2, modifions celui du champ n 1 puis revenons dans le champ n 2 (dans cet ordre). Un nouveau POST est fait vers le serveur et nous obtenons la rponse suivante :

Composants serveur - 2

23/192

Pour l'instant, tout est comme avant. Maintenant modifions le contenu du champ n 1 puis passons dans le champ n 2. Un nouveau POST est fait. La rponse du serveur est la suivante :

Cette fois-ci, il y a un changement. Le serveur a dtect un vnement [TextChanged] sur le champ n 2 puisque l'heure de [lblInfo2] a t modifie. Or il n'y avait pas de changement. Ceci s'explique par la proprit [EnableViewState=false] de [TextBox2]. Elle fait que le serveur n'a pas mis dans le champ [__VIEWSTATE] du formulaire, l'tat prcdent de [TextBox2]. Cela revient lui affecter la chane vide comme tat prcdent. Lorsque le POST du au changement du contenu de [TextBox1] s'est produit, le serveur a compar la valeur actuelle de [TextBox2] qui tait [deux] sa valeur prcdente (la chane vide). Il a en conclu que [TextBox2] avait chang de valeur et a gnr l'vnement [TextChanged] pour [TextBox2]. On peut valider ce fonctionnement en mettant la chane vide dans [TextBox2]. D'aprs ce qui vient d'tre expliqu, le serveur ne devrait pas gnrer l'vnement [TextChanged] pour [TextBox2]. Essayons :

C'est bien ce qui s'est pass. L'heure et le contenu de [lblInfo2] montrent que la procdure [TextBox2_TextChanged] n'a pas t excute. Ceci compris, examinons la proprit [EnableViewState] des quatre composants du formulaire :
TextBox1 TextBox2 lblInfo1 lblInfo2

on souhaite maintenir l'tat de ce composant afin que le serveur sache s'il a chang ou non idem on ne souhaite pas maintenir l'tat de ce composant. On veut que le texte soit recalcul chaque nouveau POST. S'il n'est pas recalcul, il doit tre vide. Tout cela est bien obtenu avec [EnableViewState=false] idem

Notre page de prsentation devient la suivante :


... <asp:TextBox id="TextBox1" runat="server" OnTextChanged="TextBox1_TextChanged" AutoPostBack="True"></asp:TextBox> ... <asp:TextBox id="TextBox2" runat="server" OnTextChanged="TextBox2_TextChanged"></asp:TextBox> ....

Composants serveur - 2

24/192

<asp:Label id="lblInfo1" runat="server" enableviewstate="False"></asp:Label> ... <asp:Label id="lblInfo2" runat="server" enableviewstate="False"></asp:Label> ...

Refaisons la mme srie de tests que prcdemment. L o nous avons obtenu l'cran

version 1

nous obtenons maintenant : version 2

Dans cette tape, on changeait le contenu du champ 1 sans changer celui du champ 2 faisant en sorte que la procdure [TextBox2_TextChanged] n'tait pas excute ct serveur ce qui impliquait que le champ [lblInfo2] ne recevait pas de nouvelle valeur. Il est donc affich avec sa valeur prcdente. Dans la version 1 [EnableViewState=true] cette valeur prcdente tait la valeur sasie. Dans la version 2 [EnableViewState=false], cette valeur prcdente est la chane vide. Il est parfois inutile de conserver l'tat prcdent des composants. Plutt que de mettre [EnableViewState=false] pour chacun d'eux on peut dclarer que la page ne doit pas maintenir son tat. Cela se fait dans la directive [Page] du code de prsentation :
<%@ Page Language="VB" EnableViewState="False" %>

Dans ce cas, quelque soit la valeur de sa proprit [EnableViewState], l'tat d'un composant n'est pas mis dans le champ cach [__VIEWSTATE]. Tout se passe alors comme si son tat prcdent tait la chane vide. Utilisons maintenant le client [curl] pour mettre en lumire d'autres mcanismes. Nous demandons tout d'abord l'url [http://localhost/form4.aspx] :
dos>curl --include --url http://localhost/form4.aspx HTTP/1.1 200 OK Server: Microsoft ASP.NET Web Matrix Server/0.6.0.0 Date: Sun, 04 Apr 2004 17:51:14 GMT Cache-Control: private Content-Type: text/html; charset=utf-8 Content-Length: 1077 Connection: Close

<html> <head> <title>asp:textbox</title> </head> <body> <form name="_ctl0" method="post" action="form4.aspx" id="_ctl0"> <input type="hidden" name="__EVENTTARGET" value="" /> <input type="hidden" name="__EVENTARGUMENT" value="" /> <input type="hidden" name="__VIEWSTATE" value="dDwtMTY4MDc0MTUxOTs7PoqpeSYSCX7lCiWZvw5p7u+/OrTD" /> <script language="javascript"> <!-function __doPostBack(eventTarget, eventArgument) { var theform = document._ctl0; theform.__EVENTTARGET.value = eventTarget; theform.__EVENTARGUMENT.value = eventArgument; theform.submit(); }

Composants serveur - 2

25/192

// --> </script> <p> Texte 1 : <input name="TextBox1" type="text" id="TextBox1" language="javascript" /> </p> <p> Texte 2 : <input name="TextBox2" type="text" id="TextBox2" /> </p> <p> <span id="lblInfo1"></span> </p> <p> <span id="lblInfo2"></span> </p> </form> </body> </html> onchange="__doPostBack('TextBox1','')"

Nous recevons du serveur le code HTML de [form4.aspx]. Il n'est pas diffrent de celui qu'avait reu le navigateur. Rappelons ici, la requte faite par le navigateur lorsqu'il postait le formulaire :
POST /form4.aspx HTTP/1.1 Connection: keep-alive ... User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.7b) Gecko/20040316 __EVENTTARGET=TextBox1&__EVENTARGUMENT=&__VIEWSTATE=dDwtMTY4MDc0MTUxOTs7PoqpeSYSCX7lCiWZvw5p7u%2B%2FOrTD&T extBox1=premier+texte&TextBox2=

Faisons ce mme POST mais sans envoyer le champ [__VIEWSTATE] :


dos>curl --include --url http://localhost/form4.aspx --data __EVENTTARGET=TextBox1 --data __EVENTARGUMENT= --data TextBox1=premier+texte --data TextBox2= ................... <p> Texte 1 : <input name="TextBox1" type="text" value="premier texte" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" /> </p> <p> Texte 2 : <input name="TextBox2" type="text" id="TextBox2" /> </p> <p> <span id="lblInfo1">19:57:48: evt [TextChanged] sur [TextBox1]. Texte 1=[premier texte]</span> </p> <p> <span id="lblInfo2"></span> </p> ..............

On notera les points suivants : le serveur a dtect un vnement [TextChanged] sur [TextBox1] puisqu'il a gnr le texte [lblInfo1]. L'absence de [__VIEWSTATE] ne l'a pas gn. En son absence, il suppose que la valeur prcdente d'un champ de saisie est la chane vide. il a t capable de remettre le texte post pour [TextBox1] dans l'attribut [value] de la balise [TextBox1] afin que le champ [TextBox1] rapparaisse avec la valeur saisie. Pour cela, il n'a pas besoin de [__VIEWSTATE] mais seulement de la valeur poste pour [TextBox1] Maintenant, refaisons la mme requte sans rien changer. Nous obtenons la nouvelle rponse suivante :
dos>curl --include --url http://localhost/form4.aspx --data __EVENTTARGET=TextBox1 --data __EVENTARGUMENT= --data TextBox1=premier+texte --data TextBox2=

<p> Texte 1 : <input name="TextBox1" type="text" value="premier onchange="__doPostBack('TextBox1','')" language="javascript" /> </p> texte" id="TextBox1"

Composants serveur - 2

26/192

<p> Texte 2 : <input name="TextBox2" type="text" id="TextBox2" /> </p> <p> <span id="lblInfo1">20:05:47: evt [TextChanged] sur [TextBox1]. Texte 1=[premier texte]</span> </p> <p> <span id="lblInfo2"></span> </p>

En l'absence de [__VIEWSTATE], le serveur n'a pas pu voir que le champ [TextBox1] n'avait pas chang de valeur. Il fait alors comme si la valeur prcdente tait la chane vide. Il a donc gnr ici l'vnement [TextChanged] sur [TextBox1]. Rejouons toujours la mme requte avec cette fois-ci le champ [TextBox1] vide et [TextBox2] non vide :
dos>curl --include --url http://localhost/form4.aspx --data __EVENTTARGET=TextBox1 --data __EVENTARGUMENT= --data TextBox2=second+texte --data TextBox1= ...... <p> Texte 1 : <input name="TextBox1" type="text" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" /> </p> <p> Texte 2 : <input name="TextBox2" type="text" value="second texte" id="TextBox2" /> </p> <p> <span id="lblInfo1"></span> </p> <p> <span id="lblInfo2">20:11:54: evt [TextChanged] sur [TextBox2]. Texte 2=[second texte]</span> </p> ......

En l'absence de [__VIEWSTATE] la valeur prcdente de [TextBox1] a t considre comme la chane vide. La valeur poste de [TextBox1] tant galement la chane vide, l'vnement [TextChanged] sur [TextBox1] n'a pas t gnr. La procdure [TextBox1_TextChanged] n'a pas t excute et donc le champ [lblInfo1] n'a pas reu de nouvelle valeur. On sait que dans ce cas, le composant garde son ancienne valeur. Or ici, ce n'est pas le cas, [lblInfo1] a perdu sa valeur prcdente. Ceci, parce que cette valeur est cherche dans [__VIEWSTATE]. Comme ce champ est absent, la chane vide a t affecte [lblInfo1]. Pour [TextBox2], le serveur a compar sa valeur poste [second texte] sa valeur prcdente. En l'absence de [__VIEWSTATE], cette valeur prcdente est gale la chaine vide. La valeur poste de [TextBox2] tant diffrente de la chane vide, l'vnement [TextChanged] sur [TextBox2] a t gnr. La procdure [TextBox2_TextChanged] a t excute et le champ [lblInfo2] a reu une nouvelle valeur. On peut se demander si les paramtres [__EVENTTARGET] et [__EVENTARGUMENT] sont bien utiles. En n'envoyant pas ces paramtres, le serveur ne saura pas par quel vnement a t provoqu le [submit]. Essayons :
dos>curl --include --url http://localhost/form4.aspx --data TextBox2=second+texte --data TextBox1=premier+texte .............................. <p> Texte 1 : <input name="TextBox1" type="text" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" /> </p> <p> Texte 2 : <input name="TextBox2" type="text" id="TextBox2" /> </p> <p> <span id="lblInfo1"></span> </p> <p> <span id="lblInfo2"></span> </p> </form> .....................

On voit qu'aucun vnement [TextChanged] n'a t trait. Par ailleurs, les champs posts [TextBox1] et [TextBox2] ne retrouvent pas leurs valeurs postes. Tout se passe en fait comme si on avait fait un GET. Tout redevient normal si on a le champ [__EVENTTARGET] dans les champs posts, mme s'il n'a pas de valeur :

Composants serveur - 2

27/192

dos>curl --include --url http://localhost/form4.aspx --data __EVENTTARGET= --data TextBox2=second+texte -data TextBox1=premier+texte ....... <p> Texte 1 : <input name="TextBox1" type="text" value="premier texte" id="TextBox1" onchange="__doPostBack('TextBox1','')" language="javascript" /> </p> <p> Texte 2 : <input name="TextBox2" type="text" value="second texte" id="TextBox2" /> </p> <p> <span id="lblInfo1">20:34:14: evt [TextChanged] sur [TextBox1]. Texte 1=[premier texte]</span> </p> <p> <span id="lblInfo2">20:34:14: evt [TextChanged] sur [TextBox2]. Texte 2=[second texte]</span> </p> ........

1.6.4 Autres proprits du composant TextBox


Le composant serveur [TextBox] permet galement de gnrer les balises HTML <input type="password"..> et <texarea>..</textarea>, c.a.d. les balises correspondant respectivement un champ de saisie protge et un champ de saisie multilignes. Cette gnration est contrle par la proprit [TextMode] du composant [TextBox]. Elle a trois valeurs possibles : valeur
SingleLine MultiLine Password

balise HTML gnre <input type="text" ...> <textarea>...</textarea> <input type="password ...>

Nous examinons l'usage de ces proprits avec l'exemple [form5.aspx] suivant : 1 1

n 1 2 3

nom
btnAjouter TextBox1 TextBox2

type Button

proprits

TextBox TextMode=Password Text= TextBox TextMode=Multilignes cumule les saisies faites dans [TextBox1] Text=

rle bouton [submit] - sert ajouter le contenu de [TextBox1] celui de [TextBox2] champ de saisie protge

La proprit [EnableViewState] de la page est mise [false]. Ct serveur, nous grons l'vnement clic sur le boutton [btnAjouter] :
Sub btnAjouter_Click(sender As Object, e As EventArgs) ' on ajoute le contenu de [textBox1] celui de [TextBox2] textbox2.text=textbox2.text + textbox1.text+controlchars.crlf End Sub

Pour comprendre ce code, il faut se rappeler le mode de traitement du POST d'un formulaire. Les procdures [Page_Init] et [Page_Load] sont excutes en premier. Puis viennent toutes les procdures d'vnements mis en cache. Enfin la procdure grant l'vnement qui a provoqu le [POST] est excute, ici la procdure [btnAjouter_Click]. Lorsque les gestionnaires d'vnements s'excutent, tous les composants de la page ayant une valeur dans le POST ont pris cette valeur. Les autres ont retrouv leur valeur prcdente si leur proprit [EnableViewState] tait [true] ou leur valeur de conception si leur proprit [EnableViewState] tait [false]. Ici, les valeurs des champs [TextBox1] et [TextBox2] vont faire partie du POST fait par le client. Aussi dans le code prcdent, [textbox1.text] aura pour valeur la valeur poste par le client, de mme pour [textbox2.text]. La procdure [btnAjouter_Click] met dans le champ [TextBox2] la valeur poste pour [TextBox2] ajoute celle poste pour [TextBox1] ajoute la marque de fin de ligne [ControlChars.CrLf] dfinie dans l'espace de noms [Microsoft.VisualBasic]. Il n'est pas ncessaire d'importer cet espace, le serveur web l'important par dfaut. Composants serveur - 2 28/192

Le code final de [form5.aspx] est le suivant :


<%@ Page Language="VB" EnableViewState="False" %> <script runat="server"> Sub btnAjouter_Click(sender As Object, e As EventArgs) ' on ajoute le contenu de [textBox1] celui de [TextBox2] textbox2.text+=textbox1.text+controlchars.crlf End Sub </script> <html> <head> </head> <body> <form runat="server"> <p> <asp:Button id="btnAjouter" onclick="btnAjouter_Click" runat="server" Text="Ajouter" EnableViewState="False"></asp:Button> <asp:TextBox id="TextBox1" runat="server" TextMode="Password" Width="353px" EnableViewState="False"></asp:TextBox> </p> <p> <asp:TextBox id="TextBox2" runat="server" TextMode="MultiLine" Width="419px" Height="121px" EnableViewState="False"></asp:TextBox> </p> </form> </body> </html>

Un peu plus haut, on a donn la copie d'cran d'une excution.

1.7 Le composant DropDownList


La balise <asp:DropDownList> permet d'insrer une liste droulante dans le code de prsentation d'une page. Nous crons une page [form6.aspx] pour obtenir la prsentation suivante : 1 2

n 1 2

nom
DropDownList1 lblInfo

type proprits rle DropDownList AutoPostback=true liste droulante EnableViewState=true Label EnableViewState=false message d'information

Le code de prsentation gnr est le suivant :


Page Language="VB" %> <script runat="server"> </script> <html> <head> </head> <body> <form runat="server"> <p> <asp:DropDownList id="DropDownList1" runat="server" OnSelectedIndexChanged="DropDownList1_SelectedIndexChanged" AutoPostBack="True"></asp:DropDownList> </p> <p> <asp:Label id="lblInfo" runat="server" enableviewstate="False"></asp:Label> </p> </form> </body> </html>

Pour l'instant, la liste droulante ne contient aucun lment. Nous allons la remplir dans la procdure [Page_Load]. Pour cela nous devons connatre certaines des proprits et mthodes de la classe [DropDownList] : Composants serveur - 2 29/192

Items Items.Count Items(i) Items.Add Items.Clear Items.RemoveAt(i) SelectedItem SelectedIndex

collection de type [ListItemCollection] des lments de la liste droulante. Les membres de cette collection dont de type [ListItem]. nombre d'lments de la collection [Items] lement n i de la liste - de type [ListItem] pour ajouter un nouvel lment [ListItem] la collection [Items] pour supprimer tous les lments de la collection [Items] pour supprimer l'lment n i de la collection [Items] 1er lment [ListItem] de la collection [Items] ayant sa proprit [Selected] vrai n de l'lment [SelectedItem] dans la collection [Items]

Les lments de la collection [Items] de la classe [DropDownList] sont de type [ListItem]. Chaque lment [ListItem] donne naissance une balise HTML <option> :
<option value="V" [selected="selected"]>T</option>

Nous dcrivons certaines proprits et mthodes de la classe [ListItem] :


ListItem(String texte, String value) Selected Text Value

constructeur - cre un lment [ListItem] avec les proprits [texte] et [value]. Un lment ListItem(T,V) donnera naissance la balise HTML <option value="V">T</option>. La classe [ListITem] permet donc de dcrire les lments d'une liste HTML boolen. Si vrai, l'option correspondante de la liste HTML aura l'attribut [selected="selected"]. Cet attribut indique au navigateur que l'lment correspondant doit appararatre slectionn dans la liste HTML le texte T de l'option HTML <option value="V" [selected="selected"]>T</option> la valeur V de l'attribut [Value] de l'option HTML <option value="V" [selected="selected"]>T</option>

Nous avons assez d'informations pour crire dans la procdure [Page_Load] de la page, le code de remplissage de la liste droulante [DropDownList1] :
Sub Page_Load(sender As Object, e As EventArgs) ' on remplit le combo si c'est la 1re fois qu'on est appel if not IsPostBack then dim valeurs() as String = {"1","2","3","4"} dim textes() as String = {"un","deux","trois","quatre"} dim i as integer for i=0 to valeurs.length-1 DropDownList1.Items.Add(new ListItem(textes(i),valeurs(i))) next end if end sub

Le composant [DropDownList1] ainsi initialis, sa traduction HTML sera la suivante :


<select name="DropDownList1" id="DropDownList1" onchange="__doPostBack('DropDownList1','')" language="javascript"> <option <option <option <option </select> value="1">un</option> value="2">deux</option> value="3">trois</option> value="4">quatre</option>

Nous savons que la procdure [Page_Load] est excute chaque excution de la page [form6.aspx]. Celle-ci est appele la 1re fois par un GET, puis par un POST chaque fois que l'utilisateur slectionne un nouvel lment dans la liste droulante. Faut-il excuter chaque fois le code de remplissage de cette liste dans [Page_Load] ? La rponse dpend de l'attribut [EnableViewState] du composant [DropDownList1]. Si cet attribut est vrai, on sait que l'tat du composant [DropDownList1] va tre conserv au fil des requtes dans le champ cach [__VIEWSTATE]. Cet tat comprend deux choses : la liste de toutes les valeurs de la liste droulante la valeur de l'lment slectionn dans cette liste Il parat alors tentant de mettre la proprit [EnableViewState] du composant [DropDownList1] [true] afin de ne pas avoir recalculer les valeurs mettre dans la liste. Le problme alors, est que la procdure [Page_Load] tant excute chaque fois que la page [form6.aspx] est demande, ces valeurs vont tre quand mme calcules. L'objet [Page] dont [form6.aspx] est une instance possde un attribut [IsPostBack] valeur boolenne. Si cet attribut est vrai, cela signifie que la page est appele par un POST. A faux, il signifie que la page a t appele par un GET. Dans notre systme d'aller-retour client-serveur, le client demande toujours la mme page [form6.aspx] au serveur. La premire fois, il la demande avec un GET, les fois suivantes avec un POST. On en conclut que la proprit [IsPostBack] peut nous servir dtecter le premier appel GET du client. On ne gnre les valeurs de la liste droulante que lors de ce premier appel. Aux requtes suivantes, ces valeurs seront gnres par le mcanisme du [VIEWSTATE]. Composants serveur - 2 30/192

Dans d'autres situations, le contenu d'une liste peut varier d'une requte l'autre et doit alors tre recalcul chacune d'elles. Dans ce cas, on mettra l'attribut [EnableViewState] de celle-ci [false] pour viter un double calcul inutile du contenu de la liste sauf si on a besoin de connatre les lments slectionns prcdemment dans la liste car cette information est conserve dans le [VIEWSTATE]. L'attribut [AutoPostBack] de la liste [DropDownList1] a t mis vrai. Cela signifie que le navigateur postera le formulaire ds qu'il dtectera l'vnement "changement de l'lment slectionn" dans la liste droulante. Le serveur son tour dtectera grce au [VIEWSTATE] et aux valeurs postes, que l'lment slectionn dans le composant [DropDownList1] a chang. Il dclenchera alors l'vnement [SelectedIndexChanged] sur ce composant. Nous le traiterons avec la procdure suivante :
Sub DropDownList1_SelectedIndexChanged(sender As Object, e As EventArgs) ' changement de slection lblInfo.text="Elment slectionn : texte="+dropdownlist1.selecteditem.text+ _ " valeur=" + dropdownlist1.selecteditem.value + _ " index="+ dropdownlist1.selectedindex.tostring End Sub

Lorsque cette procdure s'excute, l'objet [DropDownList1] a retrouv ses lments de type [ListItem] grce au [VIEWSTATE]. Par ailleurs, l'un d'eux de type [ListItem] a son attribut [Selected] vrai, celui dont la valeur a t poste par le navigateur. On a accs cet lment de plusieurs faons :
DropDownList1.SelectedItem DropDownList1.SelectedItem.Text DropDownList1.SelectedItem.Value DropdDownList1.SelectedIndex

est le 1er lment [ListItem] de la liste ayant son attribut [Selected] vrai correspond la partie [texte] de la balise HTML de l'lment <option value="...">texte</option> slectionn par l'utilisateur correspond la partie [value] de la balise HTML de l'lment <option value="...">texte</option> slectionn par l'utilisateur n dans la collection [DropDownList1.Items] du 1er lment [ListItem] ayant son attribut [Selected] vrai

Le code final de [form6.aspx] est le suivant :


<%@ Page Language="VB" %> <script runat="server"> Sub Page_Load(sender As Object, e As EventArgs) ' on remplit le combo si c'est la 1re fois qu'on est appel if not IsPostBack then dim valeurs() as String = {"1","2","3","4"} dim textes() as String = {"un","deux","trois","quatre"} dim i as integer for i=0 to valeurs.length-1 DropDownList1.Items.Add(new ListItem(textes(i),valeurs(i))) next end if end sub Sub DropDownList1_SelectedIndexChanged(sender As Object, e As EventArgs) ' changement de slection lblInfo.text="Elment slectionn : texte="+dropdownlist1.selecteditem.text+ _ " valeur=" + dropdownlist1.selecteditem.value + _ " index="+ dropdownlist1.selectedindex.tostring End Sub </script> <html> <head> </head> <body> <form runat="server"> <p> <asp:DropDownList id="DropDownList1" runat="server" OnSelectedIndexChanged="DropDownList1_SelectedIndexChanged" AutoPostBack="True"></asp:DropDownList> </p> <p> <asp:Label id="lblInfo" runat="server" enableviewstate="False"></asp:Label> </p> </form> </body> </html>

Composants serveur - 2

31/192

1.8 Le composant ListBox


La balise <asp:ListBox> permet d'insrer une liste dans le code de prsentation d'une page. Nous crons [form7.aspx] pour obtenir la prsentation suivante : 1

2 5 4 3 6 7 n 1 2 3 4 5 6 nom
txtSaisie btnAjouter ListBox1 ListBox2 btn1vers2 btn2vers1

8 type TextBox Button ListBox ListBox Button Button proprits EnableViewState=false rle champ de saisie bouton [submit] qui transfre dans Liste 1, le contenu de txtSaisie s'il est non vide. liste de valeurs slection simple

EnableViewState=true SelectionMode=Single EnableViewState=true liste de valeurs slection multiple SelectionMode=Multiple bouton [submit] qui transfre dans [liste 2] l'lment slectionn de [liste 1] bouton [submit] qui transfre dans [liste 1] les lments slectionns de [liste 2]

Le code de prsentation gnr est le suivant :


<%@ Page Language="VB" %> <script runat="server"> </script> <html> <head> </head> <body> <form runat="server"> <p> Tapez un texte pour l'inclure dans Liste 1 : <asp:TextBox id="txtSaisie" runat="server" EnableViewState="False"></asp:TextBox> </p> <p> <asp:Button id="btnAjouter" onclick="btnAjouter_Click" runat="server" Text="Ajouter"></asp:Button> </p> <p> <table> <tbody> <tr> <td> <p align="center"> Liste 1 </p> </td> <td> </td> <td> <p align="center"> Liste 2 </p> </td> </tr> <tr>

Composants serveur - 2

32/192

<td> <asp:ListBox id="ListBox1" runat="server"></asp:ListBox> </td> <td> <p> <asp:Button id="btn1vers2" onclick="btn1vers2_Click" runat="server" Text="-->"></asp:Button> </p> <p> <asp:Button id="btn2vers1" onclick="btn2vers1_Click" runat="server" Text="<--"></asp:Button> </p> </td> <td> <p> <asp:ListBox id="ListBox2" runat="server" SelectionMode="Multiple"></asp:ListBox> </p> </td> </tr> <tr> <td> <p align="center"> <asp:Button id="btnRaz1" onclick="btnRaz1_Click" runat="server" Text="Effacer"></asp:Button> </p> </td> <td> </td> <td> <p align="center"> <asp:Button id="btnRaz2" onclick="btnRaz2_Click" runat="server" Text="Effacer"></asp:Button> </p> </td> </tr> </tbody> </table> </p> </form> </body> </html>

La classe [ListBox] est drive de la mme classe [ListControl] que la classe [DropDownList] tudie prcdemment. On y retrouve toutes les proprits et mthodes vues pour [DropDownList] car elles appartenaient en fait [ListControl]. Une proprit nouvelle apparat :
SelectionMode

fixe le mode de slection de la liste HTML <select> qui sera gnre partir du composant. Si SelectionMode=Single, alors un seul lment pourra tre slectionn. Si SelectionMode=Multiple, plusieurs lments pourront tre slectionns. Pour cela, l'attribut [multiple="multiple"] sera gnr dans la balise <select> de la liste HTML.

Traitons les vnements. Le clic sur le bouton [Ajouter] sera trait par la procdure [btnAjouter_Click] suivante :
Sub btnAjouter_Click(sender As Object, e As EventArgs) ' ajout dans la liste 1 dim texte as string=txtSaisie.text.trim if texte<> "" then ListBox1.Items.Add(New ListItem(texte)) ' raz txtSaisie txtSaisie.text="" End Sub

Si le texte saisi dans [txtSaisie] n'est pas la chane vide ou blanche, un nouvel lment est ajout la liste [ListBox1]. Nous savons que nous devons ajouter un lment de type [ListItem]. Prcdemment, nous avions utilis le constructeur [ListItem(T as String, V as String)] pour faire un travail analogue. Un tel lment donne naissance la balise HTML [<option value="V">T</option>]. Ici, nous utilisons le constructeur [ListItem(T as String)] qui donne naissance la balise HTML [<option value="T">T</option>], c.a.d. que le texte [T] de l'option est pris pour former la valeur de l'option. Une fois le contenu de [txtSaisie] ajout la liste [ListBox1], le champ [txTSaisie] est vid. Les clics sur les boutons [Effacer] seront traits par les procdures suivantes :
Sub btnRaz1_Click(sender As Object, e As EventArgs) ' raz liste 1 ListBox1.Items.Clear End Sub

Composants serveur - 2

33/192

Sub btnRaz2_Click(sender As Object, e As EventArgs) ' raz liste 2 ListBox2.Items.Clear End Sub

Les clics sur les boutons de transfert entre listes sont eux traits par les procdures suivantes :
Sub btn1vers2_Click(sender As Object, e As EventArgs) ' transfert de l'lment slectionn dans liste 1 vers liste 2 transfert(ListBox1,ListBox2) End Sub Sub btn2vers1_Click(sender As Object, e As EventArgs) ' transfert de l'lment slectionn dans liste 2 vers liste 1 transfert(ListBox2,ListBox1) End Sub sub transfert(l1 as listbox, l2 as listbox) ' transferts des lments slectionns dans l1 vers l2 ' qq chose faire ? if l1.selectedindex=-1 then return dim i as integer ' on commence par la fin for i=l1.items.count-1 to 0 step -1 ' slectionn ? if l1.items(i).selected then ' plus slectionn l1.items(i).selected=false ' transfert dans l2 l2.items.add(l1.items(i)) ' suppression dans l1 l1.items.removeAt(i) end if next end sub

Parce que les deux boutons font le mme travail, transfrer des lments d'une liste une autre, on peut se ramener une unique procdure de transfert ayant deux paramtres : l1 de type [ListBox] qui est la liste source l2 de type [ListBox] qui est la liste destination Tout d'abord, on vrifie s'il y a bien au moins un lment slectionn dans la liste l1, sinon il n'y a rien faire. Pour cela, on examine la proprit [l1.selectedindex] qui reprsente le n du premier lment slectionn dans la liste. S'il n'y en a aucun, sa valeur est -1. S'il y a au moins un lment slectionn dans l1, on opre le transfert vers l2. Pour cela, on parcourt toute la liste des lments de l1 et on regarde pour chacun d'eux si son attribut [selected] est vrai. Si oui, son attribut [selected] est mis [false], puis il est recopi dans la liste l2 enfin il est supprim de la liste l1. Cette suppression provoque une renumrotation des lments de la liste l1. C'est pourquoi la liste des lments de l1 est-elle parcourue l'envers. Si on la parcourt l'endroit et qu'on supprime l'lment n 10, l'lment n 11 devient le n 10 et le n 12 l'lment n 11. Aprs avoir trait l'lment n 10, notre boucle l'endroit va traiter l'lment n 11 qui d'aprs ce qu'on vient d'expliquer est l'ancien n 12. Celui qui avait le n 11 et qui porte maintenant le n 10 nous chappe. En parcourant les lments de la liste l1 dans l'autre sens, on vite ce problme.

1.9 Les composants CheckBox, RadioButton


Les balises <asp:RadioButton> et <asp:CheckBox> permettent d'insrer respectivement un bouton radio et une case cocher dans le code de prsentation d'une page. Nous crons une page [form8.aspx] pour obtenir la prsentation suivante :

Composants serveur - 2

34/192

1 2

n 1

nom
RadioButton1 RadioButton2 RadioButton3

2 3 4

CheckBoxA CheckBoxB CheckBoxC btnEnvoyer lstInfos

type proprits RadioButton RadioButton1.Checked=true RadioButton1.Text=1 RadioButton2.Checked=false RadioButton2.Text=2 RadioButton3.Checked=false RadioButton3.Text=3 pour les 3 boutons : GroupName=radio CheckBox Checked=false pour tous CheckBoxA.Text=A CheckBoxB.Text=B CheckBoxC.Text=C Button ListBox

rle boutons radio

cases cocher bouton [submit] liste d'informations

Pour que le navigateur traite les trois boutons radio comme des boutons exclusifs les uns des autres, il faut les rassembler dans un groupe de boutons radio. Cela se fait au moyen de l'attribut [GroupName] de la classe [RadioButton]. L'tat de la page n'a pas tre maintenu dans cette application. Aussi mettons-nous l'attribut [EnableViewState="false"] la page. Le code de prsentation est le suivant :
<html> <head> </head> <body> <form id="frmControls" runat="server"> <h3>Cases cocher </h3> <p> <asp:RadioButton id="RadioButton1" runat="server" Checked="True" EnableViewState="False" GroupName="radio" Text="1"></asp:RadioButton> <asp:RadioButton id="RadioButton2" runat="server" EnableViewState="False" GroupName="radio" Text="2"></asp:RadioButton> &nbsp;<asp:RadioButton id="RadioButton3" runat="server" EnableViewState="False" GroupName="radio" Text="3"></asp:RadioButton> </p> <p> <asp:CheckBox id="CheckBoxA" runat="server" EnableViewState="False" Text="A"></asp:CheckBox> <asp:CheckBox id="CheckBoxB" runat="server" EnableViewState="False" Text="B"></asp:CheckBox> <asp:CheckBox id="CheckBoxC" runat="server" EnableViewState="False" Text="C"></asp:CheckBox> </p> <p> <asp:Button id="btnEnvoyer" onclick="btnEnvoyer_Click" runat="server" Text="Envoyer"></asp:Button> <asp:Button id="btnTree" onclick="btnTree_Click" runat="server" Text="Contrles"></asp:Button> </p> <p> <asp:ListBox id="lstInfos" runat="server" EnableViewState="False" Rows="6" Height="131px"></asp:ListBox> </p> </form> </body> </html>

Composants serveur - 2

35/192

Il nous faut crire la procdure [btnEnvoyer_Click] pour traiter l'vnement [Click] sur ce bouton. L'tat d'un bouton radio ou d'une case cocher est donn par son attribut [Checked], boolen vrai si la case est coche faux sinon. Il nous suffit donc d'crire dans la liste [lstInfos] la valeur de l'attribut [Checked] des six boutons radio et cases cocher. Comme il n'y a pas de difficult particulire cela, nous innovons un peu :
<script runat="server"> Sub btnEnvoyer_Click(sender As Object, e As EventArgs) ' on place des informations dans le listbox for each c as control in FindControl("frmControls").controls ' le contrle est-il driv de CheckBox if TypeOf(c) is CheckBox then lstInfos.Items.Add(c.ID + " : " + Ctype(c,CheckBox).Checked.ToString) end if next End Sub </script>

La page peut tre vue comme une structure arborescente de contrles. Dans notre exemple notre page comporte des textes et des contrles serveur. Les textes sont vus comme un contrle particulier appel [LiteralControl]. Le moindre texte donne naissance ce contrle mme un suite d'espaces entre deux contrles. Chaque contrle a un attribut ID qui l'identifie. C'est l'attribut ID qui appararat dans les balises :
<asp:CheckBox id="CheckBoxA" runat="server" ></asp:CheckBox>

Si nous ignorons les contrles [LiteralControl], la page tudie a les contrles suivants : - [HtmlForm] qui est le formulaire [ID=frmControls]. Celui-ci est son tour un conteneur de contrles. Il contient les contrles suivants : -- [ID=RadioButton1] de type [RadioButton] -- [ID=RadioButton2] de type [RadioButton] -- [ID=RadioButton3] de type [RadioButton] -- [ID=CheckBoxA] de type [CheckBox] -- [ID= CheckBoxA] de type [CheckBox] -- [ID= CheckBoxA] de type [CheckBox] -- [ID=btnEnvoyer] de type [Button] Un contrle possde les proprit suivantes : [Control].Controls rend la collection des contrles enfants de [Control] s'il en a [Control].FindControl(ID) rend le contrle identifi par ID se trouvant la racine de l'arborescence des contrles enfants de [Control]. Dans l'exemple ci-dessus : Page.FindControl("frmControls") dsigne le conteneur [HtmlForm]. Pour atteindre le bouton radio [RadioButton1], il faudra crire Page.FindControl("frmControls").FindControl("RadioButton1") [Control].ID identifiant de [Control] Revenons au code de la procdure [btnEnvoyer_Click] :
Sub btnEnvoyer_Click(sender As Object, e As EventArgs) ' on place des informations dans le listbox for each c as control in FindControl("frmControls").controls ' le contrle est-il driv de CheckBox ? if TypeOf(c) is CheckBox then lstInfos.Items.Add(c.ID + " : " + Ctype(c,CheckBox).Checked.ToString) end if next End Sub

On veut afficher l'tat des boutons radio et des cases cocher qui se trouvent dans le formulaire. Nous parcourons tous les contrles de celui-ci. Si le contrle courant est d'un type driv de [CheckBox] nous affichons sa proprit [Checked]. La classe [RadioButton] tant drive de la classe [CheckBox], le test est valable pour les deux types de contrles. La copie d'cran prsente plus haut montre un exemple d'excution.

1.10 Les composants CheckBoxList, RadioButtonList


Parfois, on souhaite amener l'utilisateur faire des choix entre des valeurs qu'on ne connat pas au moment de la conception de la page. Ces choix proviennent d'un fichier de configuration, d'une base de donnes, ... et ne sont connus qu'au moment de l'excution. Il existe des solutions ce problme et nous les avons rencontres. La liste slection simple convient bien lorsque Composants serveur - 2 36/192

l'utilisateur ne peut faire qu'un choix et la liste slection multiple lorsqu'il peut en faire plusieurs. D'un point de vue esthtique et si le nombre de choix n'est pas important, on peut vouloir utiliser des boutons radio la place de la liste slection simple ou des cases cocher la place de la liste slection multiple. C'est possible avec les composants [CheckBoxList] et [RadioButtonList]. Les classes [CheckBoxList] et [RadioButtonList] sont drives de la mme classe [ListControl] que les classes [DropDownList] et [ListBox] tudies prcdemment. Aussi va-t-on retrouver certaines des proprits et mthodes vues pour ces classes, celles qui appartenaient en fait [ListControl].
Items Items.Count Items(i) Items.Add Items.Clear Items.RemoveAt(i) SelectedItem SelectedIndex

collection de type [ListItemCollection] des lments de la liste droulante. Les membres de cette collection dont de type [ListItem]. nombre d'lments de la collection [Items] lement n i de la liste - de type [ListItem] pour ajouter un nouvel lment [ListItem] la collection [Items] pour supprimer tous les lments de la collection [Items] pour supprimer l'lment n i de la collection [Items] 1er lment [ListItem] de la collection [Items] ayant sa proprit [Selected] vrai n de l'lment prcdent dans la collection [Items]

Certaines proprits sont spcifiques aux classes [CheckBoxList] et [RadioButtonList] :


RepeatDirection

[horizontal] ou [vertical] pour des listes horizontales ou verticales.

Les lments de la collection [Items] sont de type [ListItem]. Chaque lment [ListItem] donnera naissance une balise diffrente selon qu'on affaire un objet [CheckBoxList] ou [RadioButtonList] :
<input type="checkbox" [selected="selected"] value="V">Texte

ou
<input type="radio" [selected="selected"] value="V">Texte

Nous dcrivons certaines proprits et mthodes de la classe [ListItem] :


ListItem(String texte, String value) Selected Text Value

constructeur - cre un lment [ListItem] avec les proprits texte et value. Un lment ListItem(T,V) donnera naissance la balise HTML <input type="checkbox" value="V">T ou <input type="radio" value="V">T selon les cas. boolen. Si vrai, l'option correspondante de la liste HTML aura l'attribut [selected="selected"]. Cet attribut indique au navigateur que l'lment correspondant doit appararatre slectionn dans la liste HTML le texte T de l'option HTML <input type=".." value="V" [selected="selected"]>T la valeur de l'attribut Value de l'option HTML <input type=".." value="V" [selected="selected"]>T

Nous nous proposons de construire la page [form8b.aspx] suivante :

1 2 3

n 1 2 3 4

nom
RadioButtonList1 CheckBoxList1 btnEnvoyer lstInfos

type proprits rle RadioButtonList EnableViewState=true liste de boutons radio RepeatDirection=horizontal CheckBoxList EnableViewState=true liste de cases cocher RepeatDirection=horizontal Button bouton [submit] qui affiche dans [4] la liste des lments slectionns dans les deux listes ListBox EnableViewState=false liste de valeurs 37/192

Composants serveur - 2

Le code de prsentation de la page est le suivant : <%@ Page


Language="VB" autoeventwireup="false" %> <script runat="server"> ... </script> <html> <head> </head> <body> <form id="frmControls" runat="server"> <h3>Listes de cases cocher </h3> <p> <asp:RadioButtonList id="RadioButtonList1" runat="server" RepeatDirection="Horizontal"></asp:RadioButtonList> </p> <p> <asp:CheckBoxList id="CheckBoxList1" runat="server" RepeatDirection="Horizontal"></asp:CheckBoxList> </p> <p> <asp:Button id="btnEnvoyer" onclick="btnEnvoyer_Click" runat="server" Text="Envoyer"></asp:Button> </p> <p> <asp:ListBox id="lstInfos" runat="server" EnableViewState="False" Rows="6"></asp:ListBox> </p> </form> </body> </html>

Le code de contrle est le suivant :


<script runat="server"> Sub Page_Load(sender As Object, e As EventArgs) handles MyBase.Load ' on remplit les listes si c'est la 1re fois qu'on est appel if not IsPostBack then ' textes pour la liste RadioButton dim textesRadio() as String = {"1","2","3","4"} ' textes pour la liste CheckBox dim textesCheckBox() as String = {"un","deux","trois","quatre"} ' remplissage liste radio dim i as integer for i=0 to textesRadio.length-1 RadioButtonList1.Items.Add(new ListItem(textesRadio(i))) next ' slection lment n 1 RadioButtonList1.SelectedIndex=1 ' remplissage liste checkbox for i=0 to textesCheckBox.length-1 CheckBoxList1.Items.Add(new ListItem(textesCheckBox(i))) next end if end sub

Sub btnEnvoyer_Click(sender As Object, e As EventArgs) ' on place des informations dans le listbox lstinfos affiche(RadioButtonList1) affiche(CheckBoxList1) End Sub sub affiche(l1 as ListControl) ' affiche les valeurs des lments slectionns de l1 ' qq chose faire ? if l1.selectedindex=-1 then return dim i as integer ' on commence par la fin for i= 0 to l1.items.count-1 ' slectionn ? if l1.items(i).selected then lstInfos.Items.Add("["+TypeName(l1)+"] ["+l1.items(i).text+"] slectionn") end if next end sub </script>

Composants serveur - 2

38/192

Dans la procdure [Page_Load] qui est excute chaque excution de la page, les deux listes sont initialises. Pour viter qu'elles le soient chaque fois, on utilise la proprit [IsPostBack] pour ne le faire que la premire fois. Les fois suivantes, les listes seront rgnres automatiquement par le mcanisme du [VIEWSTATE]. Une fois la page affiche, l'utilisateur coche certaines cases et utilise le bouton [Envoyer]. Les valeurs du formulaire sont alors postes au formulaire lui-mme. Aprs excution de [Page_Load], c'est la procdure [btnEnvoyer_Click] qui est excute. Celle-ci fait appel la procdure [affiche] pour remplir la liste [lstInfos]. Celle-ci reoit comme paramtre un objet de type [ListControl] ce qui permet de lui envoyer indiffremment un objet [RadioButtonList] ou un objet [CheckBoxList], classes drives de [ListControl]. La liste [lstInfos] peut avoir son attribut [EnableViewState] [false] puisque son tat n'a pas tre maintenu entre les diffrentes requtes.

1.11 Les composants Panel,LinkButton


La balise <asp:panel> permet d'insrer un conteneur de contrles dans une page. L'intrt du conteneur est que certaines de ses proprits s'appliquent tous les contrles qu'il contient. Il en est ainsi de sa proprit [Visible]. Cette proprit existe pour tout contrle serveur. Si un conteneur a la proprit [Visible=false], chacun de ses contrles sera contrl par sa propre proprit [Visible]. S'il a la proprit [Visible=false], alors le conteneur et tout ce qu'il contient n'est pas affich. Cela peut tre plus simple que de grer la proprit [Visible] de chacun des contrles du conteneur. La balise <asp:LinkButton> permet d'insrer un lien dans le code de prsentation d'une page. Elle a un rle analogue au bouton [Button]. Elle provoque en effet un POST ct client grce une fonction Javascript qui lui est associe. Nous crons une page [form9.aspx] pour obtenir la prsentation suivante : 1

3 n 1 2 3 nom
Panel1 ListBox1 lnkCacher

type proprits Panel EnableViewState=true ListBox EnableViewState=true LinkButton EnableViewState=false

rle conteneur de contrles une liste de trois valeurs lien pour cacher le conteneur

Lorsque le conteneur est cach, un nouveau lien apparat : 4 n 4 nom


lnkVoir

type proprits LinkButton EnableViewState=false

rle lien pour afficher le conteneur

Le code de prsentation de la page est le suivant :


<html> <head> </head> <body> <form runat="server"> <p> <asp:Panel id="Panel1" runat="server" BorderStyle="Ridge" BorderWidth="1px"> <p> Conteneur </p> <p> <asp:ListBox id="ListBox1" runat="server"> <asp:ListItem Value="1">un</asp:ListItem> <asp:ListItem Value="2">deux</asp:ListItem> <asp:ListItem Value="3" Selected="True">trois</asp:ListItem> </asp:ListBox> </p> </asp:Panel> </p> <p> <asp:LinkButton id="lnkVoir" onclick="lnkVoir_Click" runat="server">Voir le conteneur</asp:LinkButton>

Composants serveur - 2

39/192

</p> <p> <asp:LinkButton id="lnkCacher" onclick="lnkCacher_Click" runat="server">Cacher le conteneur</asp:LinkButton> </p> </form> </body> </html>

Remarquons que ce code initialise la liste [ListBox1] avec trois valeurs. Les gestionnaires des vnements [Clic] sur les deux liens sont les suivants :
<%@ Page Language="VB" %> <script runat="server"> Sub Page_Load(sender As Object, e As EventArgs) ... end sub Sub lnkVoir_Click(sender As Object, e As EventArgs) ' affiche le conteneur 1 panel1.Visible=true ' changt des liens lnkVoir.visible=false lnkCacher.visible=true End Sub Sub lnkCacher_Click(sender As Object, e As EventArgs) ' cache le conteneur 1 panel1.Visible=false ' changt des liens lnkVoir.visible=true lnkCacher.visible=false End Sub </script>

Nous utiliserons la procdure [Page_Load] pour initialiser le formulaire. Nous le ferons lors de la premire requte (IsPostBack=false) :
<%@ Page Language="VB" %> <script runat="server"> Sub Page_Load(sender As Object, e As EventArgs) ' la 1re fois if not IsPostBack then ' on montre le conteneur lnkVoir_Click(nothing,nothing) end if end sub ..... </script>

1.12 Pour poursuivre...


Les paragraphes prcdents ont prsent un certain nombre de composants serveur. Seules quelques proprits de ceux-ci ont t prsentes chaque fois. Pour approfondir l'tude de ces composants, le lecteur pourra procder de diverses faons : dcouvrir les proprits d'un composant avec un IDE tel que WebMatrix. Celui-ci expose en effet les principales proprits des composants utiliss dans un formulaire consulter la documentation de .NET pour dcouvrir l'intgralit des classes correspondant chacun des composants serveur. C'est la mthode prfrer pour une matrise totale du composant. On y dcouvrira l'arborescence des classes qui mnent aux composants, les proprits, mthodes, constructeurs, vnements de chacune d'elles. De plus la documentation fournit parfois des exemples.

Dans ce chapitre, nous avons utilis la technique [WebMatrix] du tout en un, c.a.d. qu'on a mis le code de prsentation et le code de contrle d'une page dans le mme fichier. De faon gnrale, nous ne prconisons pas cette mthode mais celle dite du [codebehind] utilise auparavant qui met ces deux codes dans deux fichiers spars. On rappelle que l'intrt de cette sparation rside dans le fait que le code de contrle peut tre compil sans avoir excuter l'application web. Par ailleurs, nos exemples et nous l'avons expliqu ds le dbut du chapitre, avaient un profil bien particulier : ils taient constitus d'une unique page qui tait un formulaire que s'changeaient le client et le serveur dans des cycles successifs de demande-rponse, la premire requte client tant un GET, et les suivantes des POST. Composants serveur - 2 40/192

1.13 Composants serveur et contrleur d'application


Dans les chapitres prcdents, nous avons construit plusieurs applications web. Elles ont toutes t construites selon l'architecture MVC (Modle-Vue-Contrleur) qui dcoupe l'aplication en blocs bien distincts et en facilite la maintenance. Nous construisions alors nos interfaces utilisateur avec des balises HTML standard. Avec ce qui vient d'tre vu, il est naturel de vouloir maintenant utiliser des composants serveur. Reprenons un problme dj tudi longuement qui tait celui du calcul d'un impt. Son architecture MVC tait la suivante : Client Interface client Logique applicative [main.aspx] CONTRLEUR Classe mtier [impot] Classe d'accs donnes [impotsData]

Donnes Sources de donnes

VUES

[formulaire.aspx] [erreurs.aspx]

L'application a deux vues : [formulaire.aspx] et [erreurs.aspx]. La vue [formulaire.aspx] est prsente lorsque l'url [main.aspx] est demande la premire fois :

L'utilisateur renseigne le formulaire :

et utilise le bouton [Calculer] pour obtenir la rponse suivante :

Composants serveur - 2

41/192

Dans une application MVC, toute requte doit passer par le contrleur, ici [main.aspx]. Cela signifie que lorsque le formulaire [formulaire.aspx] a t rempli a t rempli par l'utilisateur, il doit tre post [main.aspx] et non pas [formulaire.aspx]. Ceci n'est tout simplement pas possible si nous construisons l'interface utilisateur [formulaire.aspx] avec des composants serveurs ASP. Pour s'en rendre compte, construisons un formulaire [formtest.aspx] avec un composant <asp:button> :
<%@ Page Language="VB" EnableViewState="false"%> <html> <head> <title>test</title> </head> <body> <form action="main.aspx" runat="server"> <p> <asp:Button id="btnTest" runat="server" EnableViewState="false" Text="Test"></asp:Button> </p> </form> </body> </html>

On notera l'attribut [action="main.aspx"] de la balise <form..>. Excutons cette application. La page de prsentation ne prsente qu'un bouton :

Regardons le code HTML envoy par le serveur :


<html> <head> <title>test</title> </head> <body> <form name="_ctl0" method="post" action="formtest.aspx" id="_ctl0"> <input type="hidden" name="__VIEWSTATE" value="dDwtNTMwNzcxMzI0Ozs+" /> <p> <input type="submit" name="btnTest" value="Test" id="btnTest" /> </p> </form> </body> </html>

On voit que le POST du formulaire a pour cible le formulaire lui-mme [action="formtest.aspx"] alors que nous avions crit dans [formtest.aspx] la balise HTML serveur :
<form action="main.aspx" runat="server">

Composants serveur - 2

42/192

L'attribut [runat="server"] de la balise <form> nous est impos par l'utilisation des composants serveur. Une erreur de compilation se produit si nous ne mettons pas cet attribut. Lorsqu'on le met, alors l'attribut [action] de la balise <form> est ignore. Le serveur gnre toujours un attribut [action] qui pointe sur le formulaire lui-mme. On en dduit que dans une application MVC, on ne peut utiliser de formulaires construits avec la balise <form ... runat="server">. Or cette balise est indispensable tous les composants ASP serveur rcuprant des saisies de l'utilisateur. Autant dire qu'on ne peut utiliser de formulaires ASP serveur dans une application MVC. C'est une grande dcouverte. En effet l'un des points forts du marketing d'ASP.NET est qu'on peut construire une application web comme une application windows. C'est vrai si notre application ne respecte pas l'architecture MVC mais plus vrai sinon. Or l'architecture MVC parat une notion fondamentale du dveloppement web actuel qu'il semble difficile d'ignorer. Il est possible d'utiliser l'architecture MVC conjointement avec des formulaires composants ASP pour des applications ayant peu de vues diffrentes grce l'artifice suivant : l'application consiste en une page unique qui fait office de contrleur les vues se traduisent dans cette page par des conteneurs diffrents, un conteneur par vue. Pour afficher une vue, on rend visible son conteneur et on cache les autres C'est une solution lgante que nous mettons en oeuvre maintenant dans quelques exemples

1.14 Exemples d'applications MVC avec composants serveur ASP


1.14.1 Exemple 1
Nous mettons en oeuvre dans ce premier exemple les composants serveur que nous avons prsents. La page [form10.aspx] sera la suivante :

La copie d'cran gauche ci-dessus est le formulaire tel qu'il est prsent au client. Celui-ci le remplit et le valide par [Envoyer]. Le serveur lui renvoie une vue lui prsentant une liste des valeurs saisies (copie d'cran droite). Un lien permet l'utilisateur de retourner au formulaire. Il retrouve celui-ci tel qu'il l'a valid. Le code de prsentation de [form10.aspx] est le suivant : Composants serveur - 2 43/192

<html> <head> <title>Exemple</title> <script language="javascript"> function effacer(){ alert("Vous avez cliqu sur [Effacer]") } </script> </head> <body> <p> Gestion d'un formulaire </p> <p> <hr /> </p> <form runat="server"> <p> <asp:Panel id="panelinfo" runat="server" EnableViewState="False"> <p> Liste des valeurs obtenues </p> <p> <asp:ListBox id="lstInfos" runat="server" EnableViewState="False"></asp:ListBox> </p> <p> <asp:LinkButton id="LinkButton1" onclick="LinkButton1_Click" runat="server">Retour au formulaire</asp:LinkButton> </p> <p> <hr /> </p> </asp:Panel> </p> <p> <asp:Panel id="panelform" runat="server" > <table> <tbody> <tr> <td> Etes-vous mari(e)</td> <td> <asp:RadioButton id="rdOui" runat="server" GroupName="rdmarie"></asp:RadioButton> Oui<asp:RadioButton id="rdNon" runat="server" GroupName="rdmarie" Checked="True"></asp:RadioButton> Non</td> </tr> <tr> <td> Cases cocher</td> <td> <asp:CheckBox id="chk1" runat="server"></asp:CheckBox> 1<asp:CheckBox id="chk2" runat="server"></asp:CheckBox> 2<asp:CheckBox id="chk3" runat="server"></asp:CheckBox> 3</td> </tr> <tr> <td> Champ de saisie</td> <td> <asp:TextBox id="txtSaisie" runat="server" MaxLength="20" Columns="20"></asp:TextBox> </td> </tr> <tr> <td> Mot de passe</td> <td> <asp:TextBox id="txtmdp" runat="server" MaxLength="10" Columns="10" TextMode="Password"></asp:TextBox> </td> </tr> <tr> <td> Bote de saisie</td> <td> <asp:TextBox id="txtArea" runat="server" Columns="20" TextMode="MultiLine" Rows="3"></asp:TextBox> </td> </tr> <tr>

Composants serveur - 2

44/192

<td> Liste droulante</td> <td> <asp:DropDownList id="cmbValeurs" runat="server"></asp:DropDownList> </td> </tr> <tr> <td> Liste choix unique</td> <td> <asp:ListBox id="lstSimple" runat="server"></asp:ListBox> <asp:Button id="btnRazSimple" onclick="btnRazSimple_Click" runat="server" EnableViewState="False" Text="Raz"></asp:Button> </td> </tr> <tr> <td> Liste choix multiple</td> <td> <asp:ListBox id="lstMultiple" runat="server" SelectionMode="Multiple"></asp:ListBox> <asp:Button id="razMultiple" onclick="razMultiple_Click" runat="server" EnableViewState="False" Text="Raz"></asp:Button> </td> </tr> <tr> <td> Champ cach</td> <td> <asp:Label id="lblSecret" runat="server" visible="False"></asp:Label></td> </tr> <tr> <td> Bouton simple</td> <td> <input id="btnEffacer" onclick="effacer()" type="button" value="Effacer" /></td> </tr> <tr> <td> Bouton [reset]</td> <td> <input id="btnReset" type="reset" value="Rtablir" /></td> </tr> <tr> <td> Bouton [submit]</td> <td> <asp:Button id="btnEnvoyer" onclick="btnEnvoyer_Click" runat="server" EnableViewState="False" Text="Envoyer"></asp:Button> </td> </tr> </tbody> </table> </asp:Panel> </p> </form> </body> </html>

La page a deux conteneurs, un pour chaque vue : [panelform] pour la vue formulaire, [panelinfo] pour la vue informations. La liste des composants du conteneur [panelForm] est la suivante : nom
panelform rdOui rdNon chk1 chk2 chk3 txtSaisie txtMdp txtArea cmbValeurs lstSimple btnRazSimple

type Panel RadioButton CheckBox TextBox TextBox TextBox DropDownList ListBox Button

proprits EnableViewState=true EnableViewState=true GroupName=rdmarie EnableViewState=true EnableViewState=true EnableViewState=true EnableViewState=true EnableViewState=true EnableViewState=true SelectionMode=Single EnableViewState=false

rle vue formulaire boutons radio cases cocher champ de saisie champ de saisie protge bote de saisie multilignes liste droulante liste slection simple dslectionne tous les lments de lstSimple 45/192

Composants serveur - 2

lstMultiple btnRazMultiple lblSecret btnEffacer btnEnvoyer btnReset

ListBox Button Label HTML standard Button HTML standard

EnableViewState=true liste slection multiple SelectionMode=Multiple EnableViewState=false dslectionne tous les lments de lstMultiple EnableViewState=true champ cach Visible=false affiche une alerte EnableViewState=false bouton [submit] du formulaire bouton [reset] du formulaire

Le rle du [VIEWSTATE] pour les composants est ici important. Tous les composants sauf les boutons doivent avoir la proprit [EnableViewState=true]. Pour en comprendre la raison, il faut se rappeler le fonctionnement de l'application. Supposons que le champ [txtSaisie] ait la proprit [EnableViewState=false] : 1. 2. le client demande une premire fois la page [form10.aspx]. Il obtient la vue formulaire il le remplit et le poste avec le bouton [Envoyer]. Les champs de saisie sont alors posts et le serveur affecte aux composants serveur la valeur poste ou leur tat [VIEWSTATE] s'ils en avaient un. Ainsi le champ [txtSaisie] se voit affecte la valeur saisie par l'utilisateur. Aussi pour cette tape, son tat [VIEWSTATE] ne sert rien. Comme rsultat de l'opration, la vue [informations] est envoye, en fait toujours la page [form10.aspx] mais avec un conteneur affich diffrent. l'utilisateur consulte cette nouvelle vue et utilise le lien [Retour au formulaire] pour revenir celui-ci. Un POST est alors fait vers [form10.aspx]. Il n'y a alors au plus qu'une valeur poste : la valeur slectionne dans la liste d'informations par l'utilisateur, information non exploite par la suite. Toujours est-il qu'il n'y a a pas de champ [txtSaisie] post. le serveur reoit le POST et affecte aux composants serveur la valeur poste ou leur tat [VIEWSTATE] s'ils en avaient un. Ici, [txtNom] n'a pas de valeur poste. Si son attribut [EnableViewState] est [false], la chane vide lui sera affecte. Comme on souhaite qu'il ait la valeur saisie par l'utilisateur, il faut qu'il ait la proprit [EnableViewState=true].

3. 4.

Le conteneur [panelinfo] a les contrles suivants : nom


panelinfo lstInfos LinkButton1

type Panel ListBox LinkButton

proprits EnableViewState=false EnableViewState=false EnableViewState=false

rle vue informations liste d'informations rcapitulant les valeurs saisies par l'utilisateur lien de retour vers le formulaire

Au moment des tests, si on regarde le code HTML gnr par le code de prsentation ci-dessus, on pourra tre tonn du code gnr pour le champ cach [lblSecret] :
<tr> <td> Champ cach</td> <td></td> </tr>

Le composant [lblSecret] n'est pas traduit en code HTML car il a la proprit [Visible=false]. Cependant parce qu'il a la proprit [EnableViewState=true], sa valeur sera nanmoins conserve dans le champ cach [__VIEWSATE]. Aussi sera-t-on capable de la rcuprer comme le montreront les tests. Il nous reste crire les gestionnaires d'vnements. Dans [Page_Load] nous initialiserons le formulaire :
Sub page_Load(sender As Object, e As EventArgs) ' la 1re fois, on initialise les lments ' les fois suivantes, ceux-ci retrouvent leurs valeurs par le VIEWSTATE if IsPostBack then return ' init formulaire ' panelinfo pas affich panelinfo.visible=false ' paneform affich panelform.visible=true ' boutons radio rdNon.Checked=true ' cases cocher chk2.Checked=true ' champ de saisie txtSaisie.Text="qqs mots" ' champ mot de passe txtMdp.Text="ceciestsecret" ' bote de saisie txtArea.Text="ligne"+ControlChars.CrLf+"ligne2"+ControlChars.CrLf ' combo dim i as integer for i=1 to 4 cmbValeurs.Items.Add(new ListItem("choix"+i.ToString,i.ToString))

Composants serveur - 2

46/192

next cmbValeurs.SelectedIndex=1 ' liste slection simple for i=1 to 7 lstSimple.Items.Add(new ListItem("simple"+i.ToString,i.ToString)) next lstSimple.SelectedIndex=0 ' liste slection multiple for i=1 to 10 lstMultiple.Items.Add(new ListItem("multiple"+i.ToString,i.ToString)) next lstMultiple.Items(0).Selected=true lstMultiple.Items(2).Selected=true ' champ cach lblSecret.Text="secret" End Sub

Le clic sur les boutons [lstRazSimple] et [lstMultiple] :


Sub btnRazSimple_Click(sender As Object, e As EventArgs) ' raz liste simple lstSimple.SelectedIndex=-1 End Sub Sub razMultiple_Click(sender As Object, e As EventArgs) ' raz liste multiple lstMultiple.SelectedIndex=-1 End Sub

Le clic sur le bouton [Envoyer] :


Sub btnEnvoyer_Click(sender As Object, e As EventArgs) ' le panel info est rendu visible et le panel formulaire est cach panelinfo.Visible=true panelform.visible=false ' on rcupre les valeurs postes et on les met dans lstInfos ' boutons radio dim info as string="tat marital : "+iif(rdoui.checked,"mari"," non mari") affiche(info) ' cases cocher info=" cases coches : "+iif(chk1.checked,"1 oui","1 non")+","+ _ iif(chk2.checked,"2 oui","2 non")+","+iif(chk3.checked,"3 oui","3 non") affiche(info) ' champ de saisie affiche("champ de saisie : " + txtSaisie.Text.Trim) ' mot de passe affiche("mot de passe : " + txtMdp.Text.Trim) ' bote de saisie dim lignes() as String lignes=new Regex("\r\n").Split(txtArea.Text.Trim) dim i as integer for i=0 to lignes.length-1 lignes(i)="["+lignes(i).Trim+"]" next affiche("Bote de saisie : " + String.Join(",",lignes)) ' combo affiche("lments slectionns dans combo : "+selection(cmbValeurs)) ' liste simple affiche("lments slectionns dans liste simple : "+selection(lstSimple)) ' liste multiple affiche("lments slectionns dans liste multiple : "+selection(lstMultiple)) ' champ cach affiche ("Champ cach : " + lblSecret.Text) End Sub sub affiche(msg as String) ' affiche msg dans lstInfos lstInfos.Items.Add(msg) end sub function selection(liste as ListControl) as string ' on parcourt les lments de liste ' pour trouver ceux qui sont slectionns dim i as integer dim info as string="" for i=0 to liste.Items.Count-1 if liste.Items(i).Selected then info+="[" + liste.Items(i).Text + "]" next return info end function

Composants serveur - 2

47/192

Enfin le clic sur le lien [Retour vers le formulaire ] :


Sub LinkButton1_Click(sender As Object, e As EventArgs) ' on affiche le formulaire et on cache le panel info panelform.visible=true panelinfo.visible=false End Sub

1.14.2 Exemple 2
Nous reprenons ici, une application dj traite avec des formulaires HTML standard. L'application permet de faire des simulations de calculs d'impt. Elle s'appuie sur une classe [impot] qu'on ne rappellera pas ici. Cette classe a besoin de donnes qu'elle trouve dans une source de donnes OLEDB. Pour l'exemple, ce sera une source ACCESS.

1.14.2.1 La structure MVC de l'application


La structure MVC de l'application est la suivante :

Client Interface client

Logique applicative [main.aspx] CONTRLEUR Classe mtier [impot] Classe d'accs donnes [impotsOLEDB]

Donnes Sources de donnes

VUES

[formulaire] [erreurs]

[simulations]

Les trois vues seront incorpores dans le code de prsentation du contrleur [main.aspx] sous la forme de conteneurs. Aussi cette application a-t-elle une unique page [main.aspx].

1.14.2.2 Les vues de l'application web


La vue [formulaire] est le formulaire de saisie des informations permettant le calcul de l'impt d'un utilisateur :

L'utilisateur remplit le formulaire :

Composants serveur - 2

48/192

Il utilise le bouton [Envoyer] pour demander le calcul de son impt. Il obtient la vue [simulations] suivante :

Il retourne au formulaire par le lien ci-dessus. Il le retrouve dans l'tat o il l'a saisi. Il peut faire des erreurs de saisie :

Celles-ci lui sont signales par la vue [erreurs] :

Il retourne au formulaire par le lien ci-dessus. Il le retrouve dans l'tat o il l'a saisi. Il peut faire de nouvelles simulations :

Composants serveur - 2

49/192

Il obtient alors la vue [simulations] avec une simulation de plus :

Enfin, si la source de donnes n'est pas disponible, cela est signal l'utilisateur dans la vue [erreurs] :

1.14.2.3 Le code de prsentation de l'application


Rappelons que la page [main.aspx] rassemble toutes les vues. C'et un unique formulaire avec trois conteneurs : [panelform] pour la vue [formulaire] [panelerreurs] pour la vue [erreurs] [panelsimulations] pour la vue [simulations] Nous renouons avec la sparation code de de prsentation et code de contrle dans deux fichiers spars. Le premier sera dans [main.aspx] et le second dans [main.aspx.vb]. Le code de [main.aspx] est le suivant :
<%@ page codebehind="main.aspx.vb" inherits="vs.main" AutoEventWireUp="false" %> <HTML> <HEAD> <title>Calcul d'impt </title> </HEAD> <body> <P>Calcul de votre impt</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</TD> </TR> <TR> <TD>Nombre d'enfants</TD> <TD> <asp:TextBox id="txtEnfants" runat="server" MaxLength="3" Columns="3"></asp:TextBox></TD> </TR> <TR> <TD>Salaire annuel (euro)</TD> <TD> <asp:TextBox id="txtSalaire" runat="server" MaxLength="10" Columns="10"></asp:TextBox></TD> </TR> </TABLE> <P> <asp:Button id="btnCalculer" runat="server" Text="Calculer"></asp:Button> <asp:Button id="btnEffacer" runat="server" Text="Effacer"></asp:Button></P> </asp:panel> <asp:panel id="panelerreurs" runat="server"> <P>Les erreurs suivantes se sont produites :</P> <P> <asp:Literal id="erreursHTML" runat="server"></asp:Literal></P> <P></P> <asp:LinkButton id="lnkForm1" runat="server">Retour au formulaire</asp:LinkButton>

Composants serveur - 2

50/192

</asp:panel> <asp:panel id="panelsimulations" runat="server"> <P> <TABLE> <TR> <TH> Mari</TH> <TH> Enfants</TH> <TH> Salaire annuel</TH> <TH> Impt payer (euro)</TH></TR> <asp:Literal id="simulationsHTML" runat="server"></asp:Literal></TABLE> <asp:LinkButton id="lnkForm2" runat="server">Retour au formulaire</asp:LinkButton></P> </asp:panel> </FORM> </body> </HTML>

Nous avons dlimit les trois conteneurs. On notera qu'ils sont tous dans la balise <form runat="server">. C'est obligatoire, car si on veut pouvoir profiter des avantages des composants serveur, ceux-ci doivent tre placs dans une telle balise. Le point important saisir est qu'on a ici un unique formulaire que vont s'changer le client et le serveur web. On est donc bien dans la configuration utilise dans tout ce chapitre sur les composants serveur. Dtaillons les composants de chaque conteneur : Conteneur [panelform] : nom
panelform rdOui rdNon txtEnfants txtSalaire btnCalculer btnEffacer

type Panel RadioButton TextBox TextBox Button Button

proprits EnableViewState=true EnableViewState=true GroupName=rdmarie EnableViewState=true EnableViewState=true

rle vue formulaire boutons radio nombre d'enfants salaire annuel bouton [submit] du formulaire - lance le calcul de l'impt bouton [submit] du formulaire - vide le formulaire

Conteneur [panelerreurs] : nom


panelerreurs lnkForm1 erreursHTML

type Panel LinkButton Literal

proprits EnableViewState=true EnableViewState=true

rle vue erreurs lien vers le formulaire code HTML de la liste d'erreurs

Conteneur [panelsimulations] : nom


panelsimulations lnkForm2 simulationsHTML

type Panel LinkButton Literal

proprits EnableViewState=true EnableViewState=true

rle vue simulations lien vers le formulaire code HTML de la liste des simulations dans une table HTML

1.14.2.4 Le code de contrle de l'application


Le code de contrle de l'application se trouve rparti dans les fichiers [global.asax.vb] et [main.aspx.vb]. Le fichier [global.asax] est dfini comme suit :
<%@ Application src="Global.asax.vb" Inherits="Global" %>

Le fichier [global.asax.vb] est le suivant :


Imports Imports Imports Imports Imports Imports System System.Web System.Web.SessionState st.istia.univangers.fr System.Configuration System.Collections

Public Class Global Inherits System.Web.HttpApplication Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)

Composants serveur - 2

51/192

' on cre un objet impot Dim objImpot As impot Try objImpot = New impot(New impotsOLEDB(ConfigurationSettings.AppSettings("chaineConnexion"))) ' on met l'objet dans l'application Application("objImpot") = objImpot ' pas d'erreur Application("erreur") = False Catch ex As Exception 'il y a eu erreur, on le note dans l'application Application("erreur") = True Application("message") = ex.Message End Try End Sub Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs) ' dbut de session - on cre une liste de simulations vides Session.Item("simulations") = New ArrayList End Sub End Class

Lorsque l'application dmarre (1re requte faite l'application), la procdure [Application_Start] est excute. Elle cherche crer un objet de type [impot] prenant ses donnes dans une source OLEDB. Le lecteur est invit lire le chapitre 5 o cette classe a t dfinie, s'il l'a oublie. La construction de l'objet [impot] peut chouer si la source de donnes n'est pas disponible. Dans ce cas, l'erreur est mmorise dans l'application afin que toutes les requtes ultrieures sachent que celle-ci n'a pu s'initialiser correctement. Si la construction se passe bien, l'objet [impot] cr est lui aussi mmoris dans l'application. Il sera utilis par toutes les requtes de calcul d'impt. Lorsqu'un client fait sa premire requte, une session est cre pour lui par la procdure [Application_Start]. Cette session est destine mmoriser les diffrentes simulations de calcul d'impt qu'il va faire. Celles-ci seront mmorises dans un objet [ArrayList] associ la cl de session "simulations". Lorsque la session dmarre, cette cl est associe un objet [ArrayList] vide. Les informations ncessaires l'application sont places dans son fichier de configuration [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\serge\devel\aspnet\poly\webforms\vs\impots5\impots.mdb" /> </appSettings> </configuration>

La cl [chaineConnexion] dsigne la chane de connexion la source OLEDB. L'autre partie du code de contrle se trouve dans [main.aspx.vb] :
Imports Imports Imports Imports System.Collections Microsoft.VisualBasic st.istia.univangers.fr System

Public Class main Inherits System.Web.UI.Page Protected Protected Protected Protected Protected Protected Protected Protected Protected Protected Protected Protected Protected WithEvents WithEvents WithEvents WithEvents WithEvents WithEvents WithEvents WithEvents WithEvents WithEvents WithEvents WithEvents WithEvents rdOui As System.Web.UI.WebControls.RadioButton rdNon As System.Web.UI.WebControls.RadioButton txtEnfants As System.Web.UI.WebControls.TextBox txtSalaire As System.Web.UI.WebControls.TextBox btnCalculer As System.Web.UI.WebControls.Button btnEffacer As System.Web.UI.WebControls.Button panelform As System.Web.UI.WebControls.Panel lnkForm1 As System.Web.UI.WebControls.LinkButton lnkForm2 As System.Web.UI.WebControls.LinkButton panelerreurs As System.Web.UI.WebControls.Panel panelsimulations As System.Web.UI.WebControls.Panel simulationsHTML As System.Web.UI.WebControls.Literal erreursHTML As System.Web.UI.WebControls.Literal

' variables locales Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ... End Sub Private Sub afficheFormulaire() ... End Sub Private Sub afficheSimulations(ByRef simulations As ArrayList, ByRef lien As String) ... End Sub

Composants serveur - 2

52/192

Private Sub btnCalculer_Click(ByVal btnCalculer.Click ... End Sub

sender

As

System.Object,

ByVal

As

System.EventArgs)

Handles

Private Function checkData() As ArrayList ... End Function Private Sub lnkForm1.Click .... End Sub Private Sub lnkForm2.Click ... End Sub lnkForm1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles

lnkForm2_Click(ByVal

sender

As

System.Object,

ByVal

As

System.EventArgs)

Handles

Private Sub btnEffacer_Click(ByVal btnEffacer.Click ... End Sub Private Sub razForm() ... End Sub End Class

sender

As

System.Object,

ByVal

As

System.EventArgs)

Handles

Le premier vnement trait par le code est [Page_Load] :


Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ' tout d'abord, on regarde dans quel tat est l'application If CType(Application("erreur"), Boolean) Then ' l'application n'a pas pu s'initialiser ' on affiche la vue erreurs Dim erreurs As New ArrayList erreurs.Add("Application momentanment indisponible (" + CType(Application("message"), String) ")") afficheErreurs(erreurs, "") Exit Sub End If ' pas d'erreurs - la 1re requte, on prsente le formulaire If Not IsPostBack Then afficheFormulaire() End Sub

Rappelons que lorsque la procdure [Page_Load] s'excute sur un POST client, tous les composants du formulaire ont une valeur : soit la valeur poste par le client s'il y en a une, soit la valeur prcdente du composant grce au [VIEWSTATE]. Dans ce formulaire, tous les composants ont la proprit [EnableViewState=true]. Avant de commencer traiter la requte, nous nous assurons que l'application a pu s'initialiser correctement. Si ce n'est pas le cas, on affiche la vue [erreurs] avec la procdure [afficheErreurs]. Si c'est la premire requte (IsPostBack=false), nous faisons afficher la vue [formulaire] avec [afficheFormulaire]. La procdure affichant la vue [erreurs] est la suivante :
Private Sub afficheErreurs(ByRef erreurs As ArrayList, ByRef lien As String) ' affiche le conteneur erreurs panelerreurs.Visible = True Dim i As Integer erreursHTML.Text = "" For i = 0 To erreurs.Count - 1 erreursHTML.Text += "<li>" + erreurs(i).ToString + "</li>" + ControlChars.CrLf Next lnkForm1.Text = lien ' les autres conteneurs sont cachs panelform.Visible = False panelsimulations.Visible = False End Sub

La procdure a deux paramtres : une liste de messages d'erreurs dans [erreurs] un texte de lien dans [lien] Le code HTML gnrer pour la liste d'erreurs est plac dans le litral [erreursHTML]. Le texte du lien est lui plac dans la proprit [Text] de l'objet [LinkButton] de la vue. Composants serveur - 2 53/192

La procdure affichant la vue [formulaire] est la suivante :


Private Sub afficheFormulaire() ' affiche le formulaire panelform.Visible = True ' les autres conteneurs sont cachs panelerreurs.Visible = False panelsimulations.Visible = False End Sub

Cette procdure se contente de rendre visible le conteneur [panelform]. Les composants sont affichs avec leur valeur poste ou prcdente (VIEWSTATE). Lorsque l'utilisateur clique sur le bouton [Calculer] de la vue [formulaire], un POST vers [main.aspx] est fait. La procdure [Page_Load] est excute puis la procdure [btnCalculer_Click] :
Private Sub btnCalculer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) btnCalculer.Click ' on vrifie la validit des donnes saisies Dim erreurs As ArrayList = checkData() ' s'il y a des erreurs, on le signale If erreurs.Count <> 0 Then ' on affiche la page d'erreurs afficheErreurs(erreurs, "Retour au formulaire") Exit Sub End If ' pas d'erreurs - on calcule l'impt Dim impot As Long = CType(Application("objImpot"), impot).calculer( _ rdOui.Checked, CType(txtEnfants.Text, Integer), CType(txtSalaire.Text, Long)) ' on rajoute le rsultat aux simulations existantes Dim simulation() As String = New String() {CType(IIf(rdOui.Checked, "oui", "non"), String), _ txtEnfants.Text.Trim, txtSalaire.Text.Trim, impot.ToString} ' on rajoute le rsultat aux simulations existantes Dim simulations As ArrayList = CType(Session.Item("simulations"), ArrayList) simulations.Add(simulation) ' on met les simulations dans la session et le contexte Session.Item("simulations") = simulations ' on affiche la page de rsultat afficheSimulations(simulations, "Retour au formulaire") End Sub Handles

La procdure commence par vrifier la validit des champs du formulaire avec la procdure [checkData] qui rend une liste [ArrayList] de messages d'erreurs. Si la liste n'est pas vide, alors la vue [erreurs] est affiche et la procdure est termine. Si les donnes saisies sont valides, alors le montant de l'impt est calcul grce l'objet de type [impot] qui avait t stock dans l'application au dmarrage de celle-ci. Cette nouvelle simulation est ajoute la liste des simulations dj faites et stocke dans la session. La fonction [CheckData] vrifie la validit des donnes. Elle rend une liste [ArrayList] de messages d'erreurs, vide si les donnes sont valides :
Private Function checkData() As ArrayList ' au dpart pas d'erreurs Dim erreurs As New ArrayList ' nbre d'enfants Try Dim nbEnfants As Integer = CType(txtEnfants.Text, Integer) If nbEnfants < 0 Then Throw New Exception Catch erreurs.Add("Le nombre d'enfants est incorrect") End Try ' salaire Try Dim salaire As Long = CType(txtSalaire.Text, Long) If salaire < 0 Then Throw New Exception Catch erreurs.Add("Le salaire annuel est incorrect") End Try ' on rend la liste des erreurs Return erreurs End Function

Enfin la vue [simulations] est affiche par la procdure [afficheSimulations] suivante :


Private Sub afficheSimulations(ByRef simulations As ArrayList, ByRef lien As String) ' affiche la vue simulations panelsimulations.Visible = True

Composants serveur - 2

54/192

' les autres conteneurs sont cachs panelerreurs.Visible = False panelform.Visible = False ' contenu de la vue simulations ' chaque simulation est un tableau de 4 lments string Dim simulation() As String Dim i, j As Integer simulationsHTML.Text = "" For i = 0 To simulations.Count - 1 simulation = CType(simulations(i), String()) simulationsHTML.Text += "<tr>" For j = 0 To simulation.Length - 1 simulationsHTML.Text += "<td>" + simulation(j) + "</td>" Next simulationsHTML.Text += "</tr>" + ControlChars.CrLf Next ' lien lnkForm2.Text = lien End Sub

La procdure a deux paramtres : une liste de simulations dans [simulations] un texte de lien dans [lien] Le code HTML gnrer pour la liste de simulations est plac dans le litral [simulationsHTML]. Le texte du lien est lui plac dans la proprit [Text] de l'objet [LinkButton] de la vue. Lorsque l'utilisateur clique sur le bouton [Effacer] de la vue [formulaire], c'est la procdure [btnEffacer_click] qui s'excute (toujours aprs [Page_Load]) :
Private Sub btnEffacer_Click(ByVal btnEffacer.Click ' affiche le formulaire vide razForm() afficheFormulaire() End Sub Private Sub razForm() ' vide le formulaire rdOui.Checked = False rdNon.Checked = True txtEnfants.Text = "" txtSalaire.Text = "" End Sub sender As System.Object, ByVal e As System.EventArgs) Handles

Le code ci-dessus est suffisamment simple pour ne pas avoir tre comment. Il nous reste grer le clic sur les liens des vues [erreurs] et [simulations] :
Private Sub lnkForm1_Click(ByVal lnkForm1.Click ' affiche le formulaire afficheFormulaire() End Sub Private Sub lnkForm2_Click(ByVal lnkForm2.Click ' affiche le formulaire afficheFormulaire() End Sub sender As System.Object, ByVal e As System.EventArgs) Handles

sender

As

System.Object,

ByVal

As

System.EventArgs)

Handles

Les deux procdures se contentent de faire afficher la vue [formulaire]. Nous savons que les champs de celle-ci vont obtenir une valeur qui est soit la valeur poste pour eux soit leur valeur prcdente. Comme ici, le POST du client n'envoie aucune valeur pour les champs du formulaire, ceux-ci vont retrouver leur valeur prcdente. Le formulaire est donc affich avec les valeurs saisies par l'utilisateur. Nous nous souvenons que, dans la version sans composants serveur, nous avions fait ce travail de restauration nousmmes.

1.14.2.5 Tests
Tous les fichiers ncessaires l'application sont placs dans un Le dossier [bin] contient la dll contenant les classes [impot], dossier <application-path> : [impotsData], [impotsOLEDB] ncessaires l'application :

Composants serveur - 2

55/192

Le lecteur pourra, s'il le souhaite, relire le chapitre 5 o est explique la faon de crer le fichier [impot.dll] ci-dessus. Ceci fait, le serveur Cassini est lanc avec les paramtres (<application-path>,/impots5). On demande l'url [http://impots5/main.aspx] avec un navigateur :

Si on renomme le fichier ACCESS [impots.mdb] en [impots1.mdb], on aura la page suivante :

1.14.3 Exemple 3
Nous avons montr sur ces deux exemples qu'il tait possible de construire des applications web respectant l'architecture MVC avec des composants serveur. Le dernier exemple montre que la solution avec composants serveur est plus simple que la solution utilisant des balises HTML standard. Nos deux exemples n'avaient qu'une page avec plusieurs vues au sein de la mme page. On peut avoir une architecture MVC avec plusieurs formulaires ASP serveur tant que le fait que ceux-ci se postent eux-mmes les valeurs du formulaire ne pose pas de problme. C'est trs souvent le cas des applications avec menu. Prenons l'exemple suivant :

Composants serveur - 2

56/192

Nous avons runi dans une mme page des liens vers les applications que nous avons crites jusqu' maintenant. Ce type d'application se prte bien une architecture MVC. Simplement il n'y a plus un mais plusieurs contrleurs. Client [main.aspx] CONTRLEURS Interface client Classes mtier [appli1.aspx] [appli2.aspx] [appli3.aspx] ... Classes d'accs donnes Logique applicative

Donnes

Sources de donnes

Le contrleur [main.aspx] joue le rle de contrleur principal. C'est lui qui est appel par les liens de la page d'accueil de l'application. Il va pouvoir faire des oprations communes toutes les actions possibles puis il fera excuter l'action particulire associe au lien utilis. Il passera alors la main l'un des contrleurs secondaires, celui charg de faire excuter l'action. A partir de ce moment, les changes se font entre le client et ce contrleur particulier. On ne passe plus par le contrleur principal [main.aspx]. On n'est donc plus dans le cadre MVC avec contrleur unique qui filtre toutes les requtes. Chacun des contrleurs ci-dessus peut prsenter plusieurs vues avec le mcanisme des conteneurs au sein d'une unique page que nous avons prsent. Le fait qu'on n'ait plus un contrleur unique qui choisit les vues envoyer au client prsente des inconvnients. Prenons l'exemple de la gestion des erreurs. Chacune des actions exposes par l'application peut tre amene afficher une vue d'erreurs. Chaque contrleur [applix.aspx] va avoir sa propre vue [erreurs] parce que celle-ci est simplement un conteneur particulier de la page du contrleur. Il n'y a pas moyen d'avoir une vue [erreurs] unique qui serait utilise par toutes les applications individuelles. En effet, une telle vue prsente gnralement un lien de retour vers le formulaire erron et celui-ci doit tre restaur dans l'tat o il a t valid afin de permettre l'utlisateur de corriger ses erreurs. Cette restauration se fait par le mcanisme du [VIEWSTATE] qui ne fonctionne entre contrleurs diffrents. Si les applications sont dveloppes par des personnes diffrentes, on risque d'avoir des pages d'erreurs l'aspect diffrent selon l'action choisie par l'utilisateur entachant l'homognit de l'application globale. Nous verrons un peu plus tard, qu'ASP.NET offre une solution ce problme particulier de vue partager. Celle-ci peut faire l'objet d'un nouveau composant serveur que nous construisons nous-mmes. Il suffit d'utiliser ce composant dans les diffrentes applications pour assurer l'homgnit de l'application globale. Plus difficile grer, le problme de l'ordre des actions. Lorsque toutes les Composants serveur - 2 57/192

requtes passent par un contrleur unique, celui-ci peut vrifier que l'action demande est compatible avec la prcdente. Ce code de contrle est un unique endroit. Ici, il va falloir le rpartir sur les diffrents contrleurs compliquant la maintenance de l'application globale. Revenons notre application ci-dessus. La page d'entre de celle-ci est une page HTML classique [accueil.htm] :
<html> <head> <TITLE>Composants ASP Serveur</TITLE> <meta name="pragma" content="no-cache"> </head> <frameset rows="130,*" frameborder="0"> <frame name="banner" src="bandeau.htm" scrolling="no"> <frameset cols="200,*"> <frame name="contents" src="options.htm"> <frame name="main" src="main.htm"> </frameset> <noframes> <p id="p1"> Ce jeu de frames HTML affiche plusieurs pages Web. Pour afficher ce jeu de frames, utilisez un navigateur Web qui prend en charge HTML 4.0 et version ultrieure. </p> </noframes> </frameset> </html>

Cette page d'accueil est un ensemble de trois cadres appels banner, contents et main :

banner : bandeau.htm contents : options.htm main : main.htm

La page [bandeau.htm] place dans le cadre [banner] est la suivante :

Son code HTML est le suivant :


<html> <head> <META HTTP-EQUIV="PRAGMA" CONTENT="NO-CACHE" /> <title>bandeau</title> </head> <body> <P> <TABLE> <TR> <TD><IMG alt="logo universit d'angers" src="univ01.gif"></TD> <TD>Composants serveurs ASP</TD> </TR> </TABLE> </P> <HR> </body> </html>

La page [options.htm] est place dans le bandeau [contents]. C'est un ensemble de liens :

Composants serveur - 2

58/192

<html> <head> <meta http-equiv="pragma" content="no-cache" /> <title>options</title> </head> <body bgcolor="Gold"> <ul> <li> <a href="main.aspx?action=label" target="main">Label</a> <li> <a href="main.aspx?action=button" target="main">Button</a> <li> <a href="main.aspx?action=textbox1" target="main">TextBox-1</a></li> <li> <a href="main.aspx?action=textbox2" target="main">TextBox-2</a></li> <li> <a href="main.aspx?action=dropdownlist" target="main">DropDownList</a></li> <li> <a href="main.aspx?action=listbox" target="main">ListBox</a></li> <li> <a href="main.aspx?action=casesacocher" target="main">CheckBox</a> et<br> RadioButton</li> <li> <a href="main.aspx?action=listecasesacocher" target="main">CheckBoxList</a> et<br> RadioButtonList</a></li> <li> <a href="main.aspx?action=panel" target="main">Panel</a></li> </ul> </body> </html>

Les diffrents liens pointent tous sur le contrleur principal [main.aspx] avec un paramtre [action] indiquant l'action faire. On demande ce que la cible des liens soit affiche dans le cadre [main] (target="main"). La premire page affiche dans le cadre [main] est [main.htm] :
<html> <head> <title>main</title> </head> <body> <P>Choisissez une option pour tester dcouvrir un type de composant serveur...</P> </body> </html>

Le contrleur principal [main.aspx, main.aspx.vb] est le suivant : [main.aspx]


<%@ page src="main.aspx.vb" inherits="main" autoeventwireup="false" %>

[main.aspx.vb]
Public Class main Inherits System.Web.UI.Page Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ' on rcupre l'action faire Dim action As String If Request.QueryString("action") Is Nothing Then action = "label" Else action = Request.QueryString("action").ToString.ToLower End If ' on excute l'action Select Case action Case "label" Server.Transfer("form2.aspx") Case "button" Server.Transfer("form3.aspx") Case "textbox1" Server.Transfer("form4.aspx") Case "textbox2"

Composants serveur - 2

59/192

Server.Transfer("form5.aspx") Case "dropdownlist" Server.Transfer("form6.aspx") Case "listbox" Server.Transfer("form7.aspx") Case "casesacocher" Server.Transfer("form8.aspx") Case "listecasesacocher" Server.Transfer("form8b.aspx") Case "panel" Server.Transfer("form9.aspx") Case Else Server.Transfer("form2.aspx") End Select End Sub End Class

Notre contrleur est simple. Selon la valeur du paramtre [action], il transfre le traitement de la requte a la page adquate. Il n'apporte aucune plus-value vis vis d'une page HTML avec des liens. Nanmoins, il suffirait d'ajouter une page d'authentification pour voir son intrt. Si l'utilisateur devait s'authentifier (login, mot de passe) pour avoir accs aux applications, le contrleur [main.aspx] serait un bon endroit pour vrifier que cette authentification a t faite.

Composants serveur - 2

60/192

2 Composants serveur ASP - 2


2.1 Introduction
Nous continuons notre travail sur l'interface utilisateur en dcouvrant : des composants de validation de donnes la faon de lier des donnes des composants serveur les composants serveur HTML

2.2 Les composants de validation de donnes


2.2.1 Introduction
Dans des applications formulaires, il est primordial de vrifier la validit des donnes saisies dans ceux-ci. Dans l'exemple sur le calcul d'impt, nous avions le formulaire suivant :

A rception des valeurs de ce formulaire, il nous faut vrifier la validit des donnes saisies : le nombre d'enfants et le salaire doivent tre des entiers positifs ou nuls. Si ce n'est pas le cas, le formulaire est renvoy au client tel qu'il a t saisi avec des messages indiquant les erreurs. Nous avions trait ce cas avec deux vues, l'une pour le formulaire ci-dessus et l'autre pour les erreurs :

ASP.NET offre des composants appels composants de validation qui permettent de vrifier les cas suivants : Composant
RequiredFieldValidator CompareValidator RangeValidator RegularExpressionValidator CustomValidator ValidationSummary

Rle vrifie qu'un champ est non vide vrifie deux valeurs entre-elles vrifie q'une valeur est entre deux bornes vrifie qu'un champ vrifie une expression rgulire permet au dveloppeur de donner ses propres rgles de validation - ce composant pourrait remplacer tous les autres permet de rassembler les messages d'erreurs mis par les contrles prcdents en un unique endroit de la page

Nous allons maintenant prsenter chacun de ces composants.

2.2.2 RequiredFieldValidator
Nous construisons la page [requiredfieldvalidator1.aspx] suivante :

Composants serveur - 2

61/192

1 2

n 1 2

nom
txtNom RequiredFieldValidator1

btnEnvoyer

type proprits TextBox EnableViewState=true RequiredFieldValidator EnableViewState=false EnableClientScript=true ErrorMessage=Le champ obligatoire Button EnableViewState=false CausesValidation=true

rle champ de saisie composant de validation [nom] est bouton [submit]

Le champ [RequiredFieldValidator1] sert afficher un message d'erreur si le champ [txtNom] est vide ou contient une suite d'espaces. Ses proprits sont les suivantes :
ControlToValidate

EnableClientScript

ErrorMessage

champ dont la valeur doit tre contrle par le composant. Que signifie la valeur d'un composant ? C'est la valeur de l'attribut [value] de la balise HTML correspondante. Pour un [TextBox] ce sera le contenu du champ de saisie, pour un [DropDownList] la valeur de l'lment slectionn. boolen - vrai indique que le contenu du champ prcdent doit tre contrl galement ct client. Dans ce cas, le formulaire ne sera post par le navigateur que si le formulaire ne comporte pas d'erreurs. Nanmoins, les tests de validation sont faits galement sur le serveur pour le cas o le client ne serait pas un navigateur par exemple. le message d'erreur que le composant doit afficher en cas d'erreur dtecte

La vrification des donnes de la page par les contrles de validation n'est faite que si le bouton ou le lien ayant provoqu le [POST] de la page a la proprit [CausesValidation=true]. [true] est la valeur par dfaut de cette proprit. Excutons cette application. Nous utilisons tout d'abord un navigateur [Mozilla] :

Le code HTML reu par [Mozilla] est le suivant :


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

Composants serveur - 2

62/192

<td> Nom*</td> <td> <input name="txtNom" type="text" id="txtNom" /> &nbsp; </td> </tr> </tbody> </table> </fieldset> <p> <input type="submit" name="btnEnvoyer" value="Envoyer" onclick="if (typeof(Page_ClientValidate) == 'function') Page_ClientValidate(); " language="javascript" id="btnEnvoyer" /> </p> </form> </body> </html>

On voit que le bouton [btnEnvoyer] est li une fonction Javascript via son attribut [onclick]. On peut remarquer galement que la page ne contient aucun code [Javascript]. Le test [typeof(Page_ClientValidate) == 'function'] va chouer et la fonction javascript ne sera pas appele. Le formulaire sera alors post au serveur et c'est lui qui fera les contrles de validit. Utilisons le bouton [Envoyer] sans mettre de nom. La rponse du serveur est la suivante :

Que s'est-il pass ? Le formulaire a t post au serveur. Celui-ci a fait excuter le code de tous les composants de validation se trouvant sur la page. Ici il n'y en a qu'un. Si au moins un composant de validation dtecte une erreur, le serveur renvoie le formulaire tel qu'il a t saisi (en fait pour les composants ayant [EnableViewState=true], avec de plus un message d'erreur pour chaque composant de validation ayant dtect une erreur. Ce message est l'attribut [ErrorMessage] du composant de validation. C'est ce mcanisme que nous voyons l'oeuvre ci-dessus. Si nous mettons quelque chose dans le champ [nom], le message d'erreur n'apparat plus lorsqu'on valide le formulaire :

Maintenant, utilisons Internet Explorer et demandons l'url [http://localhost/requiredfieldvalidator1.aspx]. Nous obtenons la page suivante :

Composants serveur - 2

63/192

Le code HTML reu par IE est le suivant :


<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 bibliothque de scripts /aspnet_client/system_web/1_1_4322/WebUIValidation.js. Essayez de placer ce fichier manuellement ou effectuez une rinstallation en excutant '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 bibliothque de scripts est " + Page_ValidationVer + "."); else ValidatorOnLoad(); } function ValidatorOnSubmit() { if (Page_ValidationActive) { ValidatorCommonOnSubmit(); } } // -->

Composants serveur - 2

64/192

</script>

</form> </body> </html>

On voit que ce code est beaucoup plus important que celui reu par [Mozilla]. Nous n'entrerons pas dans les dtails. La diffrence vient du fait que le serveur a inclus des fonctions javascript dans le document HTML envoy. Pourquoi deux codes HTML diffrents alors que l'url demande par les deux navigateurs est la mme ? Nous avons eu l'occasion de dire que la technologie ASP.NET faisait que le serveur adaptait le document HTML envoy au client au niveau de ce client. Il existe sur le march diffrents navigateurs n'ayant pas toutes les mmes capacits. Les navigateurs Microsoft et Netscape par exemple, n'utilisent pas la mme modlisation objet pour le document qu'ils reoivent. Aussi le code Javascript pour manipuler ce document ct navigateur prsente-t-il des diffrences entre les deux navigateurs. De mme, les diteurs ont cr des versions successives de leur navigateur amliorant sans cesse leurs capacits. Aussi un document HTML compris par IE5 peut ne pas l'tre par IE3. Nous avons ci-dessus un exemple de cette adaptation du serveur au niveau de son client. Pour une raison qui n'a pas t approfondie ici, le serveur web a estim que le client [Mozilla] n'avait pas la capacit de grer le code javascript de validation. Aussi ce code n'a-t-il pas t plac dans le document HTML qui lui a t envoy et la validation s'est faite ct serveur. Pour [IE6], ce code javascript a t inclus dans le document HTML envoy, comme nous pouvons le voir. Pour le voir l'oeuvre, faisons l'exprience suivante : arrtons le serveur web utilisons le bouton [Envoyer] sans remplir le champ [nom] Nous obtenons la nouvelle page suivante :

C'est bien le navigateur qui a plac le message d'erreur. En effet, le serveur est arrt. On peut s'en assurer en mettant quelque chose dans le champ [nom] et en validant. Cette fois la rponse est la suivante :

Que s'est-il pass ? Le navigateur a excut le code Javascript de validation. Ce code a dit que la page tait valide. Le navigateur a donc post le formulaire au serveur web qui tait arrt. Le navigateur s'en est aperu et a affich la page ci-dessus. Si on relance le serveur, tout redevient normal. Que retenir de cet exemple ? l'intrt de la notion de composant de validation qui permet de renvoyer au client tout formulaire incorrect l'intrt du [VIEWSTATE] qui permet de renvoyer ce formulaire tel qu'il a t saisi la facult d'adaptation du serveur son client. Celui-ci est identifi par l'entte HTTP [User-Agent:] qu'il envoie au serveur. Ce n'est donc pas le serveur qui "devine" qui il a affaire. Cette facult d'adaptation prsente un grand intrt pour le dveloppeur qui n'a pas se proccuper du type de client de son application.

2.2.3 CompareValidator
Nous construisons la page [comparevalidator1.aspx] suivante :

Composants serveur - 2

65/192

1 2

3 5 n 1 2 3 nom
cmbChoix1 cmbChoix2 CompareValidator1

type DropDownList DropDownList CompareValidator

CompareValidator2

CompareValidator

btnEnvoyer

Button

proprits EnableViewState=true EnableViewState=true EnableViewState=false EnableClientScript=true ErrorMessage=Choix 1 invalide ValeurToCompare=? Operator=NotEqual Type=string EnableViewState=false EnableClientScript=true ErrorMessage=Choix 2 invalide ControlToCompare=? Operator=NotEqual Type=string EnableViewState=false CausesValidation=true

rle liste droulante liste droulante composant de validation

composant de validation

bouton [submit]

Le composant [CompareValidator] sert comparer deux valeurs entre-elles. Les oprateurs utilisables sont [Equal, NotEqual, LessThan, LessThanEqual, GreaterThan, GreaterThanEqual]. La premire valeur est fixe par la proprit [ControlToValidate], la seconde par [ValueToCompare] si la valeur prcdente doit tre compare une constante, ou par [ControlToCompare] si elle doit tre compare la valeur d'un autre composant. Les proprits importantes du composant [CompareValidator] sont les suivantes :
ControlToValidate EnableClientScript ErrorMessage ValeurToCompare ControlToCompare Operator Type

champ dont le contenu doit tre contrl par le composant boolen - vrai indique que le contenu du champ prcdent doit tre contrl galement ct client le message d'erreur que le composant doit afficher en cas d'erreur dtecte valeur laquelle doit tre compare la valeur du champ [ControlToValidate] composant la valeur duquel doit tre compare la valeur du champ [ControlToValidate] oprateur de comparaison entre les deux valeurs type des valeurs comparer

Le code de prsentation de cette page est le suivant :


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

Composants serveur - 2

66/192

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

Les contraintes de validation de la page sont les suivantes : la valeur slectionne dans [cmbChoix1] doit tre diffrente de la chane "?". Ceci implique les proprits suivantes pour le composant [CompareValidator1] : Operator=NotEqual, ValueToCompare=?, Type=string la valeur slectionne dans [cmbChoix2] doit tre diffrente de celle slectionne dans [cmbChoix1]. Ceci implique les proprits suivantes pour le composant [CompareValidator2] : Operator=NotEqual, ControlToCompare=cmbChoix1, Type=string Nous excutons cette application. Voici un exemple de page reue au cours des changes client-serveur :

Si la mme page est demande par Internet Explorer 6, du code Javascript est inclus dans celle-ci qui amne un fonctionnement lgrement diffrent. Les ventuelles erreurs sont signales ds que l'utilisateur change de valeur dans l'une des listes droulantes. C'est une amlioration du confort de l'utilisateur.

2.2.4 CustomValidator, RangeValidator


Nous construisons la page [customvalidator1.aspx] suivante :

Composants serveur - 2

67/192

1 2 7 4 6

5 8 n
1 2

nom
cmbDiplomes CompareValidator2

type DropDownList CompareValidator

proprits EnableViewState=true EnableViewState=false EnableClientScript=true ErrorMessage=Diplme invalide Operator=NotEqual ValueToCompare=? Type=String EnableViewState=false

rle liste droulante composant de validation du contrle [1]

3 4

txtAutreDiplome CustomValidator1

TextBox CustomValidator

champ de saisie

EnableViewState=false composant de EnableClientScript=true validation du champ [3] ErrorMessage=Prcision diplme invalide ClientValidationFunction=chkAutreDiplome EnableViewState=false champ de saisie

5 6

txtAnDiplome RangeValidator1

TextBox RangeValidator

EnableViewState=false composant de EnableClientScript=true validation du champ [5] ErrorMessage=Anne diplme doit tre dans l'intervalle [1990,2004] MinValue=1990 MaxValue=2004 Type=Integer ControlToValidate=txtAnDiplome composant de validation du champ [5]

RequiredFieldValidator2

RequiredFieldValidator EnableViewState=false EnableClientScript=true ErrorMessage=Anne diplme requise ControlToValidate=txtAnDiplome Button EnableViewState=false CausesValidation=true

btnEnvoyer

bouton [submit]

Le champ [RangeValidator] sert vrifier que la valeur d'un contrle se trouve entre deux bornes [MinValue] et [MaxValue]. Ses proprits sont les suivantes :
ControlToValidate EnableClientScript ErrorMessage MinValue MaxValue Type

champ dont la valeur doit tre contrle par le composant boolen - vrai indique que le contenu du champ prcdent doit tre contrl galement ct client le message d'erreur que le composant doit afficher en cas d'erreur dtecte valeur min de la valeur du champ contrler valeur max de la valeur du champ contrler type de la valeur du champ contrler

Le champ [CustomValidator] permet de faire des validations que ne savent pas faire les composants de validation proposs par ASP.NET. Cette validation est faite par une fonction crite par le dveloppeur. Celle-ci est excute ct serveur. Comme le Composants serveur - 2 68/192

contrle peut tre fait galement ct client, le dveloppeur peut tre amen dvelopper une fonction javascript qu'il inclura dans le document HTML. Ses proprits sont les suivantes :
ControlToValidate EnableClientScript ErrorMessage ClientValidationFunction

champ dont la valeur doit tre contrle par le composant boolen - vrai indique que le contenu du champ prcdent doit tre contrl galement ct client le message d'erreur que le composant doit afficher en cas d'erreur dtecte la fonction excuter ct client

Le code de prsentation de la page est le suivant :


<%@ 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 diplme--]</legend> <table> <tbody> <tr> <td> Diplme*</td> <td> <asp:DropDownList id="cmbDiplomes" runat="server"> <asp:ListItem Value="?">?</asp:ListItem> <asp:ListItem Value="[Autre]">[Autre]</asp:ListItem> <asp:ListItem Value="Ma&#238;trise">Ma&#238;trise</asp:ListItem> <asp:ListItem Value="DESS">DESS</asp:ListItem> <asp:ListItem Value="DEA">DEA</asp:ListItem> </asp:DropDownList> </td> <td> Si [Autre], prcisez</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="Diplme invalide" ControlToValidate="cmbDiplomes" ValueToCompare="?" Operator="NotEqual" ></asp:CompareValidator> </p> </td> <td> </td> <td> <asp:CustomValidator id="CustomValidator1" runat="server" ErrorMessage="Prcision diplme invalide" OnServerValidate="CustomValidator1_ServerValidate_1" EnableViewState="False" ClientValidationFunction="chckAutreDiplome"></asp:CustomValidator> </td> </tr> <tr> <td> Anne d'obtention*</td> <td colspan="3"> <asp:TextBox id="txtAnDiplome" runat="server" Columns="4"></asp:TextBox> <asp:RangeValidator id="RangeValidator1" runat="server" ErrorMessage="Anne diplme doit tre dans l'intervalle [1990,2004]" ControlToValidate="txtAnDiplome" MinimumValue="1990" MaximumValue="2004" Type="Integer"></asp:RangeValidator> <asp:RequiredFieldValidator id="RequiredFieldValidator2" runat="server" ErrorMessage="Anne diplme requise" ControlToValidate="txtAnDiplome"></asp:RequiredFieldValidator> </td> </tr> </tbody> </table> </fieldset> <p>

Composants serveur - 2

69/192

<asp:Button id="btnEnvoyer" runat="server" Text="Envoyer"></asp:Button> </p> </form> </body> </html>

L'attribut [OnServerValidate] du composant [CustomValidator] permet de prciser la fonction charge de la valisation ct serveur. Ci-dessus le composant [CustomValidator1] fait appel la procdure [CustomValidator1_ServerValidate_1] suivante :
<%@ Page Language="VB" %> <script runat="server"> Sub CustomValidator1_ServerValidate_1(sender As Object, e As ServerValidateEventArgs) ' le champ [txtAutreDiplome] doit tre non vide si [cmbDiplomes]=[autre] e.isvalid=not (cmbDiplomes.selecteditem.text="[Autre]" and txtAutreDiplome.text.trim="") _ and not (cmbDiplomes.selecteditem.text<>"[Autre]" and cmbDiplomes.selecteditem.text<>"?" and txtAutreDiplome.text.trim<>"") End Sub </script> ....

Une fonction de validation associe un composant [CustomValidator] reoit deux paramtres : sender : objet ayant provoqu l'vnement e : l'vnement. La procdure doit positionner l'attribut [e.IsValid] vrai si les donnes vrifies sont correctes, faux sinon. Ici, les vrifications faites sont les suivantes : la liste droulante [cmbDiplomes] ne peut avoir [?] pour valeur. C'est vrifi par le composant [CompareValidator2] l'utilisateur slectionne un diplme dans la liste droulante [cmbDiplomes]. Si son diplme n'existe pas dans la liste, il a la possibilit de slectionner l'option [Autre] de la liste. Il doit alors indiquer son diplme dans le champ de saisie [txtAutreDiplome]. Si [Autre] est slectionne dans [cmbDiplomes] alors le champ [txtAutreDiplome] doit tre non vide. Si ni [Autre], ni [?] n'est slectionne dans [cmbDiplomes] alors le champ [txtAutreDiplome] doit tre vide. Ceci est vrifi par la fonction associe au composant [CustomValidator1]. l'anne d'obtention du diplme doit tre dans l'intervalle [1900-2004]. Ceci est vrifi par [RangeValidator1]. Cependant si l'utilisateur ne met rien dans le champ, la fonction de validation de [RangeValidator1] n'est pas utilise. Aussi ajoute-t-on le composant [RequiredFieldValidator2] pour vrifier la prsence d'un contenu. Ceci est une rgle gnrale. Le contenu d'un champ n'est pas vrifi si celui-ci est vide. Le seul cas o il l'est, est celui o il est associ un composant [RequiredFieldValidator]. Voici un exemple d'excution dans le navigateur [Mozilla] :

Si nous utilisons le navigateur [IE6], nous pouvons ajouter une fonction de validation ct client pour le composant [CustomValidator1]. Pour cela, ce composant a les proprits suivantes : EnableClientScript=true ClientValidationFunction=chkAutreDiplome La fonction [chkAutreDiplome ] est la suivante :
<head> <meta http-equiv="pragma" content="no-cache" /> <script language="javascript">

Composants serveur - 2

70/192

function chkAutreDiplome(source,args){ // vrifie 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 fonction [chkAutreDiplome] reoit les mmes deux paramtres que la fonction serveur [CustomValidator1_ServerValidate_1] : source : l'objet qui a provoqu l'vnement args : l'vnement. Celui-ci a un attribut [IsValid] qui doit tre positionn vrai par la fonction si les donnes vrifies sont correctes. Une valeur faux affichera l'endroit du composant de validation le message d'erreur qui lui est associ et le formulaire ne sera pas post au serveur.

2.2.5 RegularExpressionValidator
Nous construisons la page [regularexpressionvalidator1.aspx] suivante :

1 2 3

4 n
1 2 3 4

nom
txtMel RequiredFieldValidator3 RegularExpressionValidator1 btnEnvoyer

type TextBox RequiredFieldValidator

proprits EnableViewState=true ControlToValidate=txtMel Display=Dynamic RegularExpressionValidator ControlToValidate=txtMel Display=Dynamic Button EnableViewState=false CausesValidation=true

rle champ de saisie composant de validation du contrle [1] composant de validation du contrle [1] bouton [submit]

Ici, nous voulons vrifier le format d'une adresse lectronique. Nous le faisons avec un composant [RegularExpressionValidator] qui permet de vrifier la validit d'un champ l'aide d'une expression rgulire. Rappelons-ici qu'une expression rgulire est un modle de chane de caractres. On vrifie donc le contenu d'un champ vis vis d'un modle. Comme le contenu d'un champ n'est pas vrifi s'il est vide, il nous faut galement un composant [RequiredFieldValidator] pour vrifier que l'adresse lectronique n'est pas vide. La classe [RegularExpressionValidator] a des proprits analogues aux classes des composants dj tudis :
ControlToValidate EnableClientScript ErrorMessage RegularExpression Display

champ dont la valeur doit tre contrle par le composant boolen - vrai indique que le contenu du champ prcdent doit tre contrl galement ct client le message d'erreur que le composant doit afficher en cas d'erreur dtecte l'expression rgulire laquelle le contenu de [ControlToValidate] va tre compar mode d'affichage : Static : le champ est toujours prsent mme s'il n'affiche pas de message d'erreur, Dynamic : le champ n'est prsent que s'il y a un message d'erreur, None : le msg d'erreur n'est pas affich. Ce champ existe galement pour les autres composants mais il n'avait pas t utilis jusqu' maintenant.

L'expression rgulire modle d'une adresse lectronique pourrait tre la suivante : \s*[\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+\s* Une adresse lectronique est de la forme [champ1.champ2....@champA.champB...]. Il y a obligatoirement au moins un champ avant le signe @ et au moins deux champs derrire. Ces champs sont constitus de caractres alphanumriques et du signe -. Le caractre alphanumrique est reprsent par le symbole \w. La squence [\w-] signifie le caractre \w ou le caractre -. Le signe + derrire Composants serveur - 2 71/192

une squence S signifie que celle-ci peut se rpter 1 ou plusieurs fois, le signe * signifie ? qu'elle peut se rpter 0 ou plusieurs fois, le signe ? qu'elle peut se rpter 0 ou 1 fois. Un champ de l'adresse lectronique correspond au modle [\w-]+. Le fait qu'il y ait obligatoirement au moins un champ avant le signe @ et au moins deux aprs, spars par le signe . correspond au modle : [\w-]+(\.[\w-]+)*@[\w-]+(\.[\w-]+)+. On peut laisser l'utilisateur mettre des espaces devant et aprs l'adresse. On les enlvera lors du traitement. Une suite d'espaces ventuellement vide est reprsente par le modle \s*. D'o l'expression rgulire de l'adresse lectonique : \s*[\w-]+(\.[\w]+)*@[\w-]+(\.[\w-]+)+\s*. Le code de prsentation de la page est alors le suivant :
<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>

2.2.6 ValidationSummary
Pour des raisons esthtiques, on peut vouloir rassembler les messages d'erreurs en un unique endroit comme dans l'exemple suivant [summaryvalidator1.aspx] o nous avons runi dans une unique page tous les exemples prcdents :

Composants serveur - 2

72/192

Tous les contrles de validation ont les proprits suivantes : [Display=Dynamic] qui fait que les contrles n'occupent pas d'espace dans la page si leur message d'erreur est vide [EnableClientScript=false] pour interdire toute validation ct client [Text=*]. Ce sera le message affich en cas d'erreur, le contenu de l'attribut [ErrorMessage] tant lui affich par le contrle [ValidationSummary] que nous prsentons ci-dessous. Cet ensemble de contrles est place dans un composant [Panel] appele [vueFormulaire]. Dans un autre composant [Panel] appel [vueErreurs], nous plaons un contrle [ValidationSummary] :

n 1 2

nom
ValidationSummary1

lnkErreursToFormulaire

type proprits ValidationSummary HeaderText=Les erreurs suivantes se sont produites, EnableClientScript=false, ShowSummary=true LinkButton CausesValidation=false

rle affiche les erreurs de tous les contrles de validation de la page lien de retour au formulaire

Pour le lien [lnkErreursToFormulaire], il n'y a pas lieu d'activer la validation puisque ce lien ne poste aucune valeur vrifier. Composants serveur - 2 73/192

Lorsque toutes les donnes sont valides, on prsente l'utilisateur la vue [infos] suivante :

n 1

nom
lblInfo

type Label

proprits

rle message d'information destination de l'utilisateur

Le code de prsentation de cette page est le suivant :


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

Voici quelques exemples de rsultats obtenus. Nous validons la vue [formulaire] sans saisir de valeurs :

Composants serveur - 2

74/192

Le bouton [Envoyer] nous donne la rponse suivante :

C'est la vue [erreurs] qui a t affiche. Si nous utilisons le lien de retour au formulaire, nous retrouvons celui-ci dans l'tat suivant :

Composants serveur - 2

75/192

C'est la vue [formulaire]. On remarquera le caractre [*] prs des donnes errones. C'est le champ [Text] des contrles de validation qui a t affich. Si nous remplissons correctement les champs, nous obtiendrons la vue [infos] :

Le code de contrle de la page est le suivant :


<%@ Page Language="VB" %> <script runat="server"> ' procdure excute au chargement de la page Sub page_Load(sender As Object, e As EventArgs) ' la 1re requte, on prsente la vue [formulaire] if not ispostback then afficheVues(true,false,false) end if end sub sub afficheVues(byval formulaireVisible as boolean, _ erreursVisible as boolean, infosVisible as boolean) ' jeu de vues 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

Composants serveur - 2

76/192

Sub CustomValidator1_ServerValidate_1(sender As Object, e As ServerValidateEventArgs) ... End Sub Sub lnkErreursToFormulaire_Click(sender As Object, e As EventArgs) ' affiche la vue formulaire afficheVues(true,false,false) ' refait les contrles de validit Page.validate End Sub Sub btnEnvoyer_Click(sender As Object, e As EventArgs) ' la page est-elle valide ? if not Page.IsValid then ' on affiche la vue [erreurs] afficheVues(false,true,false) else ' sinon la vue [infos] lblInfo.Text="Le dossier de candidature au DESS IAIE a t envoy l'adresse ["+ _ txtMel.Text + "]. Nous vous en souhaitons bonne rception.<br><br>Le secrtariat du DESS." afficheVues(false,false,true) end if end sub </script> <html> ... </html>

Dans la procdure [Page_Load] excute chaque requte du client, nous affichons la vue [formulaire], les autres tant caches. Ceci est fait seulement lors de la premire requte. La procdure fait appel une procdure utilitaire [afficheVues] qui on passe trois boolens pour faire afficher ou non les trois vues. Lorsque la procdure [btnEnvoyer_Click] est appele, les vrifications de donnes ont t faites. Le bouton [btnEnvoyer] la proprit [CausesValidation=true] qui force cette vrification des donnes. Tous les contrles de validation ont t excuts et leur proprit [IsValid] positionne. Celle-ci indique si la donne vrifie par le contrle tait valide ou non. Par ailleurs, la proprit [IsValid] de la page elle-mme a t galeemnt positionne. Elle est [vrai] uniquement si la proprit [IsValid] des tous les contrles de validation de la page ont la valeur [vrai]. Ainsi la procdure [btnEnvoyer_Click] commence-t-elle par vrifier si la page est valide ou non. Si elle est invalide, on fait afficher la vue [erreurs]. Celle-ci contient le contrle [ValidationSummary] qui reprend les attributs [ErrorMessage] de tous les contrles (voir copie d'cran plus haut). Si la page est valide, c'est la vue [infos] qui est affiche avec un message d'information. La procdure [lnkErreursToFormulaire_Click] est charge de faire afficher la vue [formulaire] dans l'tat o elle a t valide. Tous les champs de saisie de la vue [formulaire] ayant la proprit [EnableViewState=true], leur tat est automatiquement rgnr. Curieusement, l'tat des composants de validation n'est lui pas restitu. On pouvait s'attendre en effet, au retour de la vue [erreurs] voir les contrles errons afficher leur champ [Text]. Ce n'est pas le cas. On a donc forc la validation des donnes en utilisant la mthode [Page.Validate] de la page. Ceci doit tre fait une fois que le panel [vueFormulaire] a t rendu visible. Il y a donc au total deux validations. Ceci serait viter dans la pratique. Ici, l'exemple nous permettait d'introduire de nouvelles notions sur la validation de page.

2.3 Composants ListControl et liaison de donnes


Un certain nombre des composants serveur tudis permettent d'afficher une liste de valeurs (DropDownList, ListBox). D'autres que nous n'avons pas encore prsents permettent d'afficher plusieurs listes de valeurs dans des tables HTML. Pour tous, il est possible, par programme, d'associer une par une les valeurs des listes au composant associ. Il est possible galement d'associer des objets plus complexes ces composants tels des objets de type [Array], [ArrayList], [DataSet], [HashTable], ... ce qui simplifie le code qui associe les donnes au composant. On appelle cette association, une liaison de donnes. Tous les composants drivs de la classe [ListControl] peuvent tre associs une liste de donnes. Il s'agit des composants [DropDownList], [ListBox], [CheckButtonList], [RadioButtonList]. Chacun de ces composants peut tre li une source de donnes. Celle-ci peut tre diverse : [Array], [ArrayList], [DataTable], [DataSet], [HashTable],...., de faon gnrale un objet implmentant l'une des interfaces IEnumerable, ICollection, IListSource. Nous ne prsenterons que quelques-uns s'entre-eux. Un objet [DataSet] est un objet image d'une base de donnes relationnelles. C'est donc un ensemble de tables lies par des relations. L'objet [DataTable] reprsente une telle table. La source de donnes affecte les proprits [Text] et [Value] de chacun des membres [Item] de l'objet [ListControl]. Si T est la valeur de [Text] et V la valeur de [Value], la balise HTML gnre pour chaque lment de [ListControl] est la suivante :
DropDownList, ListBox CheckButtonList RadioButtonList

<option value="V">T</option> <input type="checkbox" value="V">T <input type="radio" value="V">T 77/192

Composants serveur - 2

L'association d'un composant [ListControl] une source de donnes se fait au travers des proprits suivantes :
DataSource DataMember DataTextField DataValueField

une source de donnes [Array], [ArrayList], [DataTable], [DataSet], [HashTable], ... dans le cas o la source de donnes est un [DataSet], reprsente le nom de la table utiliser comme source de donnes. La vritable source de donnes est alors une table. dans le cas o la source de donnes est une table ([DataTable],[DataSet]), reprsente le nom de la colonne de la table qui va donner ses valeurs au champ [Text] des lments du [ListControl] dans le cas o la source de donnes est une table ([DataTable],[DataSet]), reprsente le nom de la colonne de la table qui va donner ses valeurs au champ [Value] des lments du [ListControl]

L'association d'un composant [ListControl] une source de donnes n'initialise pas le composant avec les valeurs de la source de donnes. C'est l'opration [ListControl].DataBind qui le fait. Selon la nature de la source de donnes, l'association de celle-ci un composant [ListControl] se fera de faon diffrente :
Array A ArrayList AL DataTable DT

DataSet DS

HashTable HT

[ListControl].DataSource=A les champs [Text] et [Value] des lments de [ListControl] auront pour valeurs les lments de A [ListControl].DataSource=AL les champs [Text] et [Value] des lments de [ListControl] auront pour valeurs les lments de AL [ListControl].DataSource=DT, [ListControl].DataTextField="col1", [ListControl]. DatavalueField ="col2" o col1 et col2 sont deux colonnes de la table DT. Les champs [Text] et [Value] des lments de [ListControl] auront pour valeurs celles des colonnes col1 et col2 de la table DT [ListControl].DataSource=DS , [ListControl].DataSource="table" o "table" est le nom d'une des tables de DS. [ListControl].DataTextField="col1", [ListControl]. DatavalueField ="col2" o col1 et col2 sont deux colonnes de la table "table". Les champs [Text] et [Value] des lments de [ListControl] auront pour valeurs celles des colonnes col1 et col2 de la table "table" [ListControl].DataSource=HT , [ListControl].DataTextField="key", [ListControl]. DatavalueField ="value" o [key] et [value] sont respectivement les cls et les valeurs de HT.

Nous mettons en pratique ces informations sur l'exemple [databind1.aspx] suivant :

Composants serveur - 2

78/192

Nous avons l cinq liaisons de donnes avec chaque fois quatre contrles de type [DropDownList], [ListBox], [CheckBoxList] et [RadioButtonList]. Les cinq liaisons tudies diffrent par leurs sources de donnes : liaison source de donnes Array ArrayList DataTable DataSet HashTable

1 2 3 4 5

2.3.1 Code de prsentation des composants


Le code de prsentation des contrles de la liaison 1 est le suivant :
<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>

Celui des liaisons 2 5 est identique au numro de liaison prs.

2.3.2 Liaison une source de donnes de type Array


La liaison des quatre contrles [ListBox] ci-dessus se fait de la faon suivante dans la procdure [Page_Load] du code de contrle :
' les donnes globales 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 ' procdure excute au chargement de la page Sub page_Load(sender As Object, e As EventArgs) if not IsPostBack then ' on cre les sources des donnes qu'on va lier aux composants createDataSources ' liaison un tableau [Array] bindToArray ' liaison une liste [ArrayList] bindToArrayList ' liaison une table [DataTable] bindToDataTable ' liaison un groupe de donnes [DataSet] bindToDataSet ' liaison un dictionnaire [HashTable] bindToHashTable end if End Sub sub createDataSources ' cre les sources de donnes qui seront lies aux composants ' arraylist dim i as integer for i=0 to textes.length-1 myDataListe.add(textes(i)) next ' datatable ' on dfinit ses deux colonnes myDataTable.Columns.Add("id",Type.GetType("System.Int32")) myDataTable.Columns.Add("texte",Type.GetType("System.String")) ' on remplit la table dim ligne as DataRow for i=0 to textes.length-1 ligne=myDataTable.NewRow

Composants serveur - 2

79/192

ligne("id")=i ligne("texte")=textes(i) myDataTable.Rows.Add(ligne) next ' dataset - une seule table myDataSet.Tables.Add(myDataTable) ' hashtable for i=0 to textes.length-1 myHashTable.add(valeurs(i),textes(i)) next end sub ' liaison tableau sub bindToArray ' l'association aux composants 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 ' la slection des lments 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

La liaison des contrles aux donnes ne se fait ici qu' la premire requte. Ensuite les contrles garderont leurs lments par le mcanisme du [VIEWSTATE]. La liaison est faite dans la procdure [bindToArray]. La source de donnes tant de type [Array], seul le champ [DataSource] des composants [ListControl] est initialis. Le remplissage du contrle avec les valeurs de la source de donnes associe est ralise par la mthode [ListControl].DataBind. Ce n'est qu'aprs, que les objets [ListControl] ont des lments. On peut alors slectionner certains d'entre-eux.

2.3.3 Liaison une source de donnes de type ArrayList


La source de donnes [myDataListe] est initialise dans la procdure [createDataSources] :
' les donnes globales dim textes() as string={"un","deux","trois","quatre"} dim myDataListe as new ArrayList .. ' procdure excute au chargement de la page Sub page_Load(sender As Object, e As EventArgs) if not IsPostBack then ' on cre les sources des donnes qu'on va lier aux composants createDataSources ... end if End Sub sub createDataSources ' cre les sources de donnes qui seront lies aux composants

Composants serveur - 2

80/192

' arraylist dim i as integer for i=0 to textes.length-1 myDataListe.add(textes(i)) next ... end sub

La liaison des quatre contrles [ListBox] de la liaison 2 se fait de la faon suivante dans la procdure [bindToArrayList] du code de contrle :
' 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 slection des lments 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

La source de donnes tant de type [ArrayList], seul le champ [DataSource] des composants [ListControl] est initialis.

2.3.4 Source de donnes de type DataTable


La source de donnes [myDataTable] est initialise dans la procdure [createDataSources] :
' les donnes globales dim textes() as string={"un","deux","trois","quatre"} dim myDataTable as new DataTable("table1") ... ' procdure excute au chargement de la page Sub page_Load(sender As Object, e As EventArgs) if not IsPostBack then ' on cre les sources des donnes qu'on va lier aux composants createDataSources ... end if End Sub sub createDataSources ' cre les sources de donnes qui seront lies aux composants ... ' datatable ' on dfinit ses deux colonnes myDataTable.Columns.Add("id",Type.GetType("System.Int32")) myDataTable.Columns.Add("texte",Type.GetType("System.String")) ' on remplit la 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

Composants serveur - 2

81/192

On commence par construire un objet [DataTable] avec deux colonnes [id] et [texte]. La colonne [id] alimentera le champ [Value] des lments des [ListControl] et la colonne [texte] leurs champs [Text]. La construction du [DataTable] avec deux colonnes se fait de la faon suivante :
myDataTable.Columns.Add("id",Type.GetType("System.Int32")) myDataTable.Columns.Add("texte",Type.GetType("System.String"))

On cre donc une table deux colonnes : la premire appele "id" est de type entier la seconde appele "texte" est de type chane de caractres La structure de la table tant cre, on peut la remplir avec le code suivant :
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 colonne [id] va contenir des entiers [0,1,..,n] alors que la colonne [texte] va contenir les valeurs du tableau [data]. Ceci fait, la table [dataListe] est remplie. La liaison des quatre contrles [ListBox] de la liaison 3 se fait de la faon suivante dans la procdure [bindToDataTable] du code de contrle :
' liaison datatable 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 slection des lments 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

Chaque composant [ListControl] est li la source [myDataTable] en affectant pour chacun d'entre-eux :
.DataSource= myDataTable .DataValueField="id" .DataTextField="texte"

La table [myDataTable] est la source de donnes. La colonne [id] de cette table va alimenter les champs [Value] des lments des composants alors que la colonne [texte] va alimenter leurs champs [Text].

2.3.5 Source de donnes de type DataSet


La source de donnes [myDataSet] est initialise dans la procdure [createDataSources] : Composants serveur - 2 82/192

' les donnes globales dim myDataTable as new DataTable("table1") dim myDataSet as new DataSet ...

' procdure excute au chargement de la page Sub page_Load(sender As Object, e As EventArgs) if not IsPostBack then ' on cre les sources des donnes qu'on va lier aux composants createDataSources ... end if End Sub sub createDataSources ' cre les sources de donnes qui seront lies aux composants ' dataset - une seule table myDataSet.Tables.Add(myDataTable) ... end sub

Un objet [DataSet] reprsente un ensemble de tables de type [DataTable]. On ajoute la table [myDataTable] construite prcdemment au [DataSet]. La liaison des quatre contrles [ListBox] de la liaison 4 se fait de la faon suivante dans la procdure [bindToDataSet] du code de contrle :
' liaison dataset 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 slection des lments 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

Chaque composant [ListControl] est li la source de donnes de la faon suivante :


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

Le groupe de donnes [myDataSet] est la source de donnes. Comme celle-ci peut comprendre plusieurs tables, on prcise dans [DataMember] le nom de celle que l'on va utiliser. La colonne [id] de cette table va alimenter les champs [Value] des lments des composants alors que la colonne [texte] va alimenter leurs champs [Text].

Composants serveur - 2

83/192

2.3.6 Source de donnes de type HashTable


La source de donnes [myHashTable] est initialise dans la procdure [createDataSources] :
' les donnes globales dim textes() as string={"un","deux","trois","quatre"} dim valeurs() as string={"1","2","3","4"} dim myHashTable as new HashTable ... ' procdure excute au chargement de la page Sub page_Load(sender As Object, e As EventArgs) if not IsPostBack then ' on cre les sources des donnes qu'on va lier aux composants createDataSources ... end if End Sub sub createDataSources ' cre les sources de donnes qui seront lies aux composants ... ' hashtable for i=0 to textes.length-1 myHashTable.add(valeurs(i),textes(i)) next end sub

Le dictionnaire [myHashTable] peut tre vu comme une table deux colonnes appeles "key" et "value". La colonne [key] reprsente les cls du dictionnaire et la colonne [value] les valeurs associes celles-ci. La colonne [key] est ici forme du contenu du tableau [valeurs] et la colonne [value] du contenu du tableau [textes]. La liaison de cette source aux contrles est faite dans la procdure [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 slection des lments 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

Pour chacun des composants, la liaison est faite par les instructions :
.DataSource=myHashTable .DataValueField="key" .DataTextField="value"

Composants serveur - 2

84/192

La source de donnes est le dictionnaire [myHashTable]. Les valeurs du contrle sont fournies par la colonne [key] du dictionnaire et les textes par la colonne [value]. L'insertion des lments du dictionnaire dans les contrles se fait dans l'ordre des cls qui est priori alatoire.

2.3.7 Les directives d'importation d'espaces de noms


Un certain nombre d'espaces de noms sont automatiquement imports dans une page ASP.NET. Ce n'est pas le cas de "System.Data" o se trouve les classes [DataTable] et [DataSet]. Aussi faut-il importer cette classe. Cela se fait de la faon suivante :
<%@ Page Language="VB" %> <%@ import Namespace="System.Data" %> <script runat="server"> ... </script> <html> ... </html>

2.4 Composant DataGrid et liaison de donnes


Le composant [DataGrid] permet d'afficher des donnes sous forme de tableaux mais il va bien au-del de ce simple affichage : il offre la possibilit de paramtrer de faon prcise le "rendu visuel" du tableau il permet la mise jour de la source de donnes Le composant [DataGrid] est un composant la fois puissant et complexe. Nous allons le prsenter par touches successives.

2.4.1 Affichage d'une source de donnes Array, ArrayList, DataTable, DataSet


Le composant [DataGrid] permet d'afficher dans un tableau HTML des sources de donnes de type [Array], [ArrayList], [DataTable], [DataSet]. Pour ces quatre type de donnes, il suffit d'associer la source la proprit [DataSource] du composant [DataGrid] :
DataSource DataMember

une source de donnes [Array], [ArrayList], [DataTable], [DataSet], ... dans le cas o la source de donnes est un [DataSet], reprsente le nom de la table utiliser comme source de donnes. La vritable source de donnes est alors une table. Si ce champ n'est pas renseign, toutes les tables du [DataSet] sont affiches.

Nous prsentons maintenant la page [datagrid1.aspx] qui montre l'association d'un [DataGrid] quatre sources de donnes diffrentes :

La page contient quatre composants [DataGrid] construits avec [WebMatrix] de la faon suivante. On dpose le composant son emplacement dans l'onglet [Design] :

Composants serveur - 2

85/192

Un tableau HTML gnrique est alors dessin. Les proprits d'un [DataGrid] peuvent tre dfinies la conception. C'est ce que nous faisons ici pour ses proprits de mise en forme. Pour cela, nous slectionnons le [DataGrid] configurer. Ses proprits apparaissent dans une fentre en bas droite :

Nous allons utiliser les deux liens ci-dessus. Le lien [Gnrateur de proprits] donne accs aux principales proprits du [DataGrid] :

Nous dcochons la phrase [Afficher l'en-tte] pour les quatre composants [DataGrid] et validons la page. L'autre lien [Mise en forme automatique] permet de choisir parmi plusieurs styles pour le tableau HTML qui sera affich : Composants serveur - 2 86/192

Nous choisissons [couleur i] pour le [DataGrid] n i. Ces choix de conception sont traduits dans le code de prsentation de la page :
<html> <head> </head> <body> <form runat="server"> <p> Liaison de donnes 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>

Composants serveur - 2

87/192

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

En mode conception, nous n'avons fix que des proprits de mise en forme. C'est dans le code de contrle que nous associons des donnes aux quatre composants :
<%@ Page Language="VB" %> <%@ import Namespace="system.data" %> <script runat="server"> ' les donnes globales 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 ' procdure excute au chargement de la page Sub page_Load(sender As Object, e As EventArgs) if not IsPostBack then ' on cre les sources des donnes qu'on va lier aux composants createDataSources ' liaison un tableau [Array] bindToArray ' liaison une liste [ArrayList] bindToArrayList ' liaison une table [DataTable] bindToDataTable ' liaison un groupe de donnes [DataSet] bindToDataSet end if End Sub sub createDataSources ' cre les sources de donnes qui seront lies aux composants ' arraylist dim i as integer for i=0 to textes1.length-1 myDataListe.add(textes1(i)) next ' datatable ' on dfinit ses deux colonnes myDataTable.Columns.Add("id",Type.GetType("System.Int32")) myDataTable.Columns.Add("texte1",Type.GetType("System.String")) myDataTable.Columns.Add("texte2",Type.GetType("System.String")) ' on remplit la table dim ligne as DataRow for i=0 to textes1.length-1 ligne=myDataTable.NewRow ligne("id")=valeurs(i)

Composants serveur - 2

88/192

ligne("texte1")=textes1(i) ligne("texte2")=textes2(i) myDataTable.Rows.Add(ligne) next ' dataset - une seule table myDataSet.Tables.Add(myDataTable) end sub ' liaison tableau sub bindToArray with DataGrid1 .DataSource=textes1 .DataBind end with end sub ' liaison arraylist sub bindToArrayList with DataGrid2 .DataSource=myDataListe .DataBind end with end sub ' liaison datatable sub bindToDataTable with DataGrid3 .DataSource=myDataTable .DataBind end with end sub ' liaison dataset sub bindToDataSet with DataGrid4 .DataSource=myDataSet .DataBind end with end sub </script> <html> ... </html>

Le code est assez semblable celui de l'exemple prcdent, aussi ne le commenterons-nous pas particulirement. Remarquons cependant comment la liaison de donnes est faite. Pour chacun des quatre contrle, la squence suivante suffit :
with [DataGrid] .DataSource=[source de donnes] .DataBind end with

o [source de donnes] est de type [Array] ou [ArrayList] ou [DataTable] ou [DataSet]. On voit donc qu'on a l, un outil puissant d'affichage de donnes en tableaux : la mise en forme est faite avec [WebMatrix] ou tout autre IDE au moment de la conception la liaison de donnes est faite dans le code. Avec [WebMatrix], elle peut tre faite au moment de la conception si la source de donnes est une base SQL server ou une base ACCESS. Dans ce cas, on placera un composant de type [SqlDataSource] ou [AccessDataSource] sur la feuille. Ce composant peut tre reli au moment de la conception la source physique des donnes, une base SQL Server ou une base ACCESS selon les cas. Si on affecte la proprit[DataSource] d'un composant [DataGrid] un objet [SqlDataSource] ou [AccessDataSource] reli une source physique, alors on verra apparatre, en mode conception, les donnes relles dans le composant [DataGrid].

2.5 ViewState des composants listes de donnes


Nous nous proposons ici de mettre en lumire le mcanisme du [VIEWSATE] pour les composants listes de donnes. On peut hsiter entre deux mthodes pour maintenir l'tat d'un composant liste de donnes entre deux requtes du client : mettre son attribut [VIEWSTATE] vrai mettre son attribut [VIEWSTATE] faux et mmoriser sa source de donnes dans la session afin d'tre capable de relier dle composant cette source lors de la prochaine requte. L'exemple suivant illustre certains aspects du mcanisme du [VIEWSTATE] des conteneurs de donnes. A la premire requte du client, l'application prsente la vue suivante : Composants serveur - 2 89/192

n 1 2 3

nom
DataGrid1 DataGrid2 Button1

type DataGrid DataGrid Button

proprits EnableViewState=true EnableViewState=true EnableViewState=false

rle affiche une source de donnes S affiche la mme source S que [DataGrid1] bouton [submit]

Le composant [DataGrid1] est maintenu par le mcanisme du [VIEWSTATE]. On cherche savoir si ce mcanisme qui rgnre l'affichage de [DataGrid1] chaque requte, rgnre galement sa source de donnes. Pour cela, celle-ci est lie au composant [DataGrid2]. La gnration de celui-ci a chaque requte est faite par une liaison explicite la source de de donnes de [DataGrid1]. Il a son attribut [EnableViewState] [vrai] galement. Le code de prsentation [main.aspx] de l'application est le suivant :
<%@ 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>

Le contrleur [main.aspx.vb] est lui le suivant :


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 ' la 1re requte la source de donnes est dfinie et lie au 1er datagrid If Not IsPostBack Then 'dfinir la source de donnes

Composants serveur - 2

90/192

With DataGrid1 .DataSource = createDataSource() .DataBind() End With End If ' chaque requte, on lie datagrid2 avec la source du 1er datagrid With Datagrid2 .DataSource = DataGrid1.DataSource .DataBind() End With End Sub Private Function createDataSource() As DataTable ' on initialise la source de donnes Dim thmes As New DataTable ' colonnes With thmes.Columns .Add("id", GetType(System.Int32)) .Add("thme", GetType(System.String)) .Add("description", GetType(System.String)) End With ' colonne id sera cl primaire thmes.Constraints.Add("clprimaire", thmes.Columns("id"), True) ' lignes Dim ligne As DataRow For i As Integer = 0 To 4 ligne = thmes.NewRow ligne.Item("id") = i.ToString ligne.Item("thme") = "thme" + i.ToString ligne.Item("description") = "description du thme " + i.ToString thmes.Rows.Add(ligne) Next Return thmes End Function Private Sub InitializeComponent() End Sub End Class

La mthode [createDataSource] cre une source S de type [DataTable]. Nous ne nous attarderons pas sur son code qui n'est pas l'objet de l'exemple. C'est la faon utilise pour construire les deux composants [DataGrid] qui nous intressent : le composant [DataGrid1] est li une fois la table S, lors de la premire requte. Il ne l'est plus ensuite. le composant [DataGrid2] est li la source [DataGrid1.DataSource] chaque nouvelle requte. Lors de la premire requte; nous obtenons la vue suivante :

Assez logiquement, les deux composants affichent la source de donnes laquelle ils ont t lis. Nous utilisons le bouton [Envoyer] pour provoquer un [PostBAck] vers le serveur. La vue obtenue est alors la suivante :

Composants serveur - 2

91/192

Nous constatons que le composant [DataGrid1] a conserv sa valeur mais pas le composant [DataGrid2]. Explications : avant mme que la procdure [Page_Load] ne dmarre, les objets [DataGrid1] et [DataGrid2] ont rcupr la valeur qu'ils avaient lors de la requte prcdente, cause du mcanisme du [viewstate]. Ils ont en effet, tous les deux, leur proprit [EnableViewState] [vrai]. la procdure [Page_Load] s'excute. Comme on a affaire une opration [PostBack], le composant [DataGrid1] n'est pas modifi par [Page_Load] (cf code). Aussi garde-t-il la valeur rcupre grce au [viewstate]. C'est ce que montre l'cran cidessus. le composant [DataGrid2] est lui reli [DataBind] la source de donnes [DataGrid1.DataSource]. Il est donc reconstruit et la valeur qu'il venait de rcuprer grce au [viewstate] est perdue. Il y aurait donc eu intrt ici ce qu'il ait sa proprit [EnableViewState] [faux] afin d'viter la gestion inutile de son tat. L'cran ci-dessus montre que [DataGrid2] a t li une source vide. Cette source tant [DataGrid1.DataSource], on en dduit que si le mcanisme du [viewstate] restaure bien l'affichage du composant [DataGrid1], il ne restaure pas pour autant ses proprits telles que [DataSource]. Que conclure de cet exemple ? Il faut viter de mettre la proprit [EnableViewState] d'un conteneur de donnes [vrai] si celui-ci doit tre li (DataBind) une source de donnes chaque requte. Il y a cependant certains cas, o mme dans ce cas de figure, la proprit [EnableViewState] du conteneur doit tre maintenue [vrai], sinon des vnements que l'on voudrait grer ne sont pas dclenchs. Nous aurons l'occasion d'en rencontrer un exemple. Frquemment, la source de donnes d'un conteneur de donnes volue au fil des requtes. Il faut donc, chaque requte, relier le conteneur la source de donnes. Il est frquent que celle-ci soit mise en session afin que les requtes y aient accs. Notre second exemple montre ce mcanisme. L'application ne fournit qu'une seule vue :

1 2

4 3 n 1 2 3 4 nom
DataGrid1 DataGrid2 Button1 lblInfo1 lblInfo2 lblInfo3

type DataGrid DataGrid Button Label

proprits EnableViewState=true EnableViewState=false EnableViewState=false EnableViewState=false

rle affiche une source de donnes S affiche la mme source S que [DataGrid1] bouton [submit] textes d'information

Composants serveur - 2

92/192

Le composant [DataGrid1] est li aux donnes uniquement lors de la premire requte. Il gardera sa valeur au fil des requtes grce au mcanisme du [viewstate]. Le composant [DataGrid2] est li chaque requte une source de donnes qui on ajoute un lment chaque nouvelle requte. Il faut donc relier (DataBind) le composant [DataGrid2] chaque requte. On a donc mis son attribut [EnableViewState] [faux] comme il a t conseill prcdemment. Ainsi lors de la seconde requte (usage du bouton [Envoyer]), on a la rponse suivante :

Le composant [DataGrid1] a gard sa valeur initiale. Le composant [DataGrid2] a un lment de plus. Les trois informations [1,2,2] reprsentent le n de requte. On voit que l'une des informations est errone. Nous essaierons de comprendre pourquoi. Le code de prsentation [main.aspx] de l'application est le suivant :
<%@ 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>Numro de requte&nbsp;: <asp:Label id="lblInfo1" runat="server" EnableViewState="False"></asp:Label>, <asp:Label id="lblInfo2" runat="server" EnableViewState="False"></asp:Label>, <asp:Label id="lblInfo3" runat="server" EnableViewState="False"></asp:Label></P> <P> <asp:Button id="Button1" runat="server" Text="Envoyer" EnableViewState="False"></asp:Button></P> </form> </body> </HTML>

Le code du contrleur [main.aspx.vb] est le suivant :


Imports System.Data Imports System Public Class main Inherits System.Web.UI.Page

Composants serveur - 2

93/192

Protected Protected Protected Protected Protected Protected Dim Dim Dim Dim

WithEvents WithEvents WithEvents WithEvents WithEvents WithEvents

DataGrid1 As System.Web.UI.WebControls.DataGrid Datagrid2 As System.Web.UI.WebControls.DataGrid Button1 As System.Web.UI.WebControls.Button lblInfo1 As System.Web.UI.WebControls.Label lblInfo2 As System.Web.UI.WebControls.Label lblInfo3 As System.Web.UI.WebControls.Label

dtThmes As numRequte1 numRequte2 numRequte3

DataTable As Integer As Integer As New entier

Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ' la 1re requte la source de donnes est dfinie et lie au 1er datagrid If Not IsPostBack Then 'dfinir la source de donnes dtThmes = createDataSource() With DataGrid1 .DataSource = dtThmes .DataBind() End With ' mmoriser des informations dans la session Session("source") = dtThmes numRequte1 = 0 : Session("numRequte1") = numRequte1 numRequte2 = 0 : Session("numRequte2") = numRequte2 numRequte3.valeur = 0 : Session("numRequte3") = numRequte3 End If ' chaque requte, on ajoute un nouveau thme dtThmes = CType(Session("source"), DataTable) Dim nbThmes = dtThmes.Rows.Count Dim ligne As DataRow = dtThmes.NewRow With ligne .Item("id") = nbThmes.ToString .Item("thme") = "thme" + nbThmes.ToString .Item("description") = "description du thme " + nbThmes.ToString End With dtThmes.Rows.Add(ligne) 'lie datagrid2 avec la source de donnes With Datagrid2 .DataSource = dtThmes .DataBind() End With ' infos nbre de requtes numRequte1 = CType(Session("numRequte1"), Integer) numRequte1 += 1 lblInfo1.Text = numRequte1.ToString numRequte2 = CType(Session("numRequte2"), Integer) numRequte2 += 1 lblInfo2.Text = numRequte2.ToString numRequte3 = CType(Session("numRequte3"), entier) numRequte3.valeur += 1 lblInfo3.Text = numRequte3.valeur.ToString ' on mmorise qqs infos dans la session Session("numRequte2") = numRequte2 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

Nous n'avons pas reproduit le code de la mthode [createDataSource]. C'est le mme que dans l'application prcdente au dtail prs qu'on ne met que trois lignes dans la source. Intressons-nous tout d'abord la gestion de la source de donnes et des deux conteneurs :
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ' la 1re requte la source de donnes est dfinie et lie au 1er datagrid If Not IsPostBack Then 'dfinir la source de donnes

Composants serveur - 2

94/192

dtThmes = createDataSource() With DataGrid1 .DataSource = dtThmes .DataBind() End With ' mmoriser des informations dans la session Session("source") = dtThmes ... End If ' chaque requte, on ajoute un nouveau thme dtThmes = CType(Session("source"), DataTable) Dim nbThmes = dtThmes.Rows.Count Dim ligne As DataRow = dtThmes.NewRow With ligne .Item("id") = nbThmes.ToString .Item("thme") = "thme" + nbThmes.ToString .Item("description") = "description du thme " + nbThmes.ToString End With dtThmes.Rows.Add(ligne) 'lie datagrid2 avec la source de donnes With Datagrid2 .DataSource = dtThmes .DataBind() End With ... End Sub

Le composant [DataGrid1] n'est li la source S de donnes que lors de la premire requte (not IsPostBack). Celle-ci est alors place dans la session. Elle ne le sera plus par la suite. La source de donnes S mise en session est rcupre chaque requte et se voit ajouter une nouvelle ligne. Le composant [DataGrid2] est li explicitement, chaque requte, la source S. C'est pourquoi son contenu augmente d'une ligne chaque requte. On constate qu'aprs avoir modifi le contenu de la source S, on ne remet pas explicitement celle-ci en session par une opration :
Session("source") = S

Pourquoi ? Lorsqu'une requte dmarre, la session a en son sein un objet Session("source") de type [DataTable] qui est la source de donnes telle qu'elle tait lors de la dernire requte. Appelons S l'objet Session("source"). Lorsque nous crivons :
dtThmes = CType(Session("source"), DataTable)

[dtThmes] et [S] sont deux rfrences sur un mme objet [DataTable]. Ainsi lorsque le code de [Page_Load] ajoute un lment la table rfrence par [dtThmes], il l'ajoute du mme coup la table rfrence par [S]. A la fin d'excution de la page, tous les objets prsents dans la session vont tre sauvegards et donc l'objet Session("source"), c.a.d. S, c.a.d. [dtThmes]. C'est donc bien le nouveau contenu de la source de donnes qui est sauvegarde. Il n'y a pas eu besoin d'crire :
Session("source") = dtThmes

pour faire cette sauvegarde, car Session("source") est dj gal [dtThmes]. Ceci n'est plus vrai lorsque les donnes mises en session ne sont pas des objets, telles les structures [Integer, Float, ...]. C'est ce que montre la gestion des compteurs de requtes :
Dim numRequte1 As Integer Dim numRequte2 As Integer Dim numRequte3 As New entier Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ' la 1re requte la source de donnes est dfinie et lie au 1er datagrid .... ' mmoriser des informations dans la session Session("source") = dtThmes numRequte1 = 0 : Session("numRequte1") = numRequte1 numRequte2 = 0 : Session("numRequte2") = numRequte2 numRequte3.valeur = 0 : Session("numRequte3") = numRequte3 End If ' chaque requte, on ajoute un nouveau thme .... ' infos nbre de requtes numRequte1 = CType(Session("numRequte1"), Integer) numRequte1 += 1 lblInfo1.Text = numRequte1.ToString numRequte2 = CType(Session("numRequte2"), Integer) numRequte2 += 1 lblInfo2.Text = numRequte2.ToString numRequte3 = CType(Session("numRequte3"), entier) numRequte3.valeur += 1 lblInfo3.Text = numRequte3.valeur.ToString ' on mmorise qqs infos dans la session

Composants serveur - 2

95/192

Session("numRequte2") = numRequte2 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

On mmorise les compteurs de requtes dans trois lments : numRequte1 et numRequte2 de type [Integer] - [Integer] n'est pas une classe mais une structure numRequte3 de type [entier] - [entier] est une classe dfinie pour l'occasion Lorsqu'on crit :
numRequte1 = CType(Session("numRequte1"), Integer) .. numRequte2 = CType(Session("numRequte2"), Integer) .. numRequte3 = CType(Session("numRequte3"), entier) ..

la structure [Session("numRequte1")] est recopie dans [numRequte1]. Ainsi lorsque l'lment [numRequte1] est modifi, l'lment [Session("numRequte1")] lui, ne l'est pas il en est de mme pour [Session("numRequte2")] et [numRequte2] ls lments [Session("numRequte3")] et [numRequte3] sont eux deux rfrences un mme objet de type [entier]. L'objet rfrenc peut tre modifi indiffremment par l'une ou l'autre rfrence.

Ce ceci, on dduit qu'il est inutile d'crire :


Session("numRequte3") = numRequte3

pour mmoriser la nouvelle valeur de [numRequte3] dans la session. En revanche, il faudrait crire :
Session("numRequte1") = numRequte1 Session("numRequte2") = numRequte2

pour mmoriser les nouvelles valeurs des structures [numRequte1] et [numRequte2]. Nous ne le faisons que pour [numRequte2], ce qui explique que sur la copie d'cran obtenue l'issue de la seconde requte, le compteur [numRequte1] est erron. On retiendra donc qu'une fois mise en session, une donne n' a pas y tre remise de faon rpte si elle est reprsente par un objet. Dans les autres cas, il faut le faire si elle a t modifie.

2.6 Affichage d'une liste de donnes l'aide d'un DataGrid pagin et tri
Le composant [DataGrid] permet d'afficher le contenu d'un [DataSet]. Pour l'instant, nous avons toujours construit nos [DataSet] " la main". Cette fois, nous utilisons un [DataSet] issu d'une base de donnes. Nous construisons l'application MVC suivante :

Composants serveur - 2

96/192

Client Interface client

Logique applicative [global.asax], [main.aspx] CONTRLEUR Classe d'accs aux donnes [produits]

Donnes Sources de donnes

VUES

[formulaire] [erreurs]

[rsultats]

Les trois vues seront incorpores dans le code de prsentation du contrleur [main.aspx] sous la forme de conteneurs. Aussi cette application a-t-elle une unique page [main.aspx].

2.6.1 Les classes mtier


La classe [produits] donne accs la base ACCESS suivante :

Le code de la classe [produits] est le suivant :


Imports Imports Imports Imports System System.Data System.Data.OleDb System.Xml

Namespace st.istia.univangers.fr Public Class produits Private chaineConnexionOLEDB As String Public Sub New(ByVal chaineConnexionOLEDB As String) ' on mmorise la chane de connexion Me.chaineConnexionOLEDB = chaineConnexionOLEDB End Sub Public Function getDataSet(ByVal commande As String) As DataSet ' on cre un objet DataAdapter pour lire les donnes de la source OLEDB Dim adaptateur As New OleDbDataAdapter(commande, chaineConnexionOLEDB) ' on cre une image en mmoire du rsultat du select Dim contenu As New DataSet Try adaptateur.Fill(contenu) Catch e As Exception Throw New Exception("Erreur d'accs la base de donnes (" + e.Message + ")") End Try ' on rend le rsultat Return contenu End Function End Class End Namespace

La base ACCESS sera gre via un pilote OLEDB. On donne au constructeur de la classe [produits], la chane de connexion identifiant et le pilote OLEDB et la base que l'on doit grer. La classe [produits] a une mthode [getDataSet] qui rend un [DataSet] obtenu par excution d'une requte SQL [select] dont le texte est pass en paramtre. Au cour de la mthode, plusieurs oprations ont lieu : cration de la connexion la base, excution du [select], fermeture de la connexion. Tout ceci peut gnrer une exception qui est ici gre. Un programme de test pourrait tre le suivant :
Option Explicit On Option Strict On

Composants serveur - 2

97/192

' espaces de noms Imports System Imports System.Data Imports Microsoft.VisualBasic Namespace st.istia.univangers.fr ' pg de test Module testproduits Sub Main(ByVal arguments() As String) ' affiche le contenu d'une table de produits ' la table est dans une base ACCESS dont le pg reoit le nom de fichier Const syntaxe1 As String = "pg bdACCESS" ' vrification des paramtres du programme If arguments.Length <> 1 Then ' msg d'erreur Console.Error.WriteLine(syntaxe1) ' fin Environment.Exit(1) End If ' on prpare la chane de connexion Dim chaineConnexion As String = "Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=" + arguments(0) ' cration d'un objet produits Dim objProduits As produits = New produits(chaineConnexion) ' on rcupre la table des produits dans un 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 ' on affiche son contenu Dim lignes As DataRowCollection = contenu.Tables(0).Rows For i As Integer = 0 To lignes.Count - 1 ' ligne i de la table 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

La compilation des diffrents lments de cette application se fait de la faon suivante :


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 07/05/2004 07/04/2004 07/05/2004 07/05/2004

16:52 11:07 07:01 14:21 14:22

118 784 902 1 532 3 584 4 608

produits.mdb produits.vb testproduits.vb produits.dll testproduits.exe

dos>testproduits produits.mdb 1,produit1,10 2,produit2,20 3,produit3,30

2.6.2 Les vues


Maintenant que nous avons la classe d'accs aux donnes, nous crivons les contrleurs et vues de cette application web. Voyons tout d'abord son fonctionnement. La premire vue est la suivante :

Composants serveur - 2

98/192

3 6 n 1 2 3 4 5 6 nom
txtSelect RequiredFieldValidator1 txtPages

type proprits TextBox EnableViewState=true RequiredFieldValidator EnableViewState=false TextBox EnableViewState=true RequiredFieldValidator EnableViewState=false RangeValidator EnableViewState=false Button EnableViewState=false

RequiredFieldValidator2 RangeValidator1 btnExcuter

rle champ de saisie de la requte select vrifie la prsence de 1 champ de saisie - indique le nombre de lignes de donnes afficher par page de rsultats vrifie la prsence de 3 vrifie que (3) est dans l'intervalle [1,30] bouton [submit]

Nous appellerons cette vue, la vue [formulaire]. Elle permet l'utilisateur de faire excuter une requte SQL select sur la base [produits.mdb]. L'excution de la requte donne naissance une nouvelle vue :

1 2

n 1 2 3 4

nom
lblSelect rdCroissant rdDcroissant DataGrid1

lnkRsultats

type proprits Label EnableViewState=false RadioButton EnableViewState=false GroupName=rdTri DataGrid EnableViewState=true AllowPaging=true AllowSorting=true LinkButton EnableViewState=false

rle champ d'information permet de choisir un sens de tri table d'affichage du rsultat du select lien-bouton [submit]

Nous appellerons cette vue, la vue [rsultats]. C'est elle qui contient le [DataGrid] qui va afficher le rsultat de la commande SQL select. L'utilisateur peut se tromper dans sa requte. Certaines erreurs lui seront signales dans la vue [erreurs] grce aux contrles de validation. Composants serveur - 2 99/192

D'autres erreurs lui seront signales par la vue [erreurs] :

La vue [erreurs] est affiche :

2 n 1 3 nom
erreursHTML lnkForm2

type variable LinkButton

proprits EnableViewState=false

rle code HTML ncessaire l'affichage des erreurs lien-bouton [submit]

Les trois vues de l'application sont trois conteneurs (panel) diffrents au sein de la mme page [main.aspx]. Son code de prsentation est le suivant :
<%@ Page src="main.aspx.vb" inherits="main" autoeventwireup="false" Language="vb" %> <HTML> <HEAD> </HEAD> <body> <P>Liaison de donnes avec un DataGrid</P> <HR width="100%" SIZE="1"> <form runat="server"> <asp:panel id="vueFormulaire" runat="server"> <P>Commande [select] excuter sur la table LISTE</P> <P>&nbsp;Exemple : select id, nom, prix from LISTE </P> <P>

Composants serveur - 2

100/192

<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 requte [select] excuter" 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 dsires" 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="btnExcuter" runat="server" EnableViewState="False" Text="Excuter"></asp:Button></P> </asp:panel> <asp:Panel id="vueRsultats" runat="server"> <P>Rsultats de la requte <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="rdDcroissant" runat="server" Text="dcroissant" GroupName="rdTri"></asp:RadioButton></P> <P> <asp:DataGrid id="DataGrid1" runat="server" BorderColor="#CC9966" BorderStyle="None" BorderWidth="1px" BackColor="White" CellPadding="4" AllowPaging="True" PageSize="4" AllowSorting="True"> <SelectedItemStyle Font-Bold="True" ForeColor="#663399" BackColor="#FFCC66"></SelectedItemStyle> <ItemStyle ForeColor="#330099" BackColor="White"></ItemStyle> <HeaderStyle Font-Bold="True" ForeColor="#FFFFCC" BackColor="#990000"></HeaderStyle> <FooterStyle ForeColor="#330099" BackColor="#FFFFCC"></FooterStyle> <PagerStyle NextPageText="Suivant" PrevPageText="Pr&#233;c&#233;dent" HorizontalAlign="Center" ForeColor="#330099" BackColor="#FFFFCC"></PagerStyle> </asp:DataGrid></P> <P> <asp:LinkButton id="lnkRsultats" 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>

2.6.3 Configuration du DataGrid


Attardons-nous sur la pagination du composant [DataGrid], option que nous rencontrons pour la premire fois. Dans le code cidessus, cette pagination est contrle par les attributs suivants :
<asp:DataGrid id="DataGrid1" runat="server" PageSize="4" AllowPaging="True" ...> ... <PagerStyle NextPageText="Suivant" PrevPageText="Pr&#233;c&#233;dent" ...></PagerStyle> </asp:DataGrid></P> AllowPaging="true" PageSize="4" NextPageText="Suivant" PrevPageText="Prcdent"

autorise la pagination quatre lignes de donnes par page Le texte du lien pour aller la page suivante de la source de donnes Le texte du lien pour aller la page prcdente de la source de donnes

Ces informations peuvent tre saisies directement dans les attributs de la balise <asp:datagrid>. On peut galement s'aider de [WebMatrix]. Dans la fentre de proprits du [DataGrid], on suit le lien [Gnrateur de proprits] :

Composants serveur - 2

101/192

On obtient l'assistant suivant :

On prend l'option [Pagination] :

Nous retrouvons ci-dessus, les valeurs des attributs de pagination du [DataGrid] du code de prsentation. Par ailleurs, nous autorisons le tri des donnes sur l'une des colonnes du [DataGrid]. Il y a diffrentes faons de faire cela. L'une d'elles est de dfinir la proprit [AllowSorting=true] dans la fentre de proprits du [DataGrid]. On peut aussi le gnrateur de proprits. Quelque soit la mthode utilise, cela se traduit par la prsence de l'attribut [AllowSorting=true] dans la balise <asp:DataGrid> du composant :
<asp:DataGrid id="DataGrid1" runat="server" ... AllowPaging="True" PageSize="4" AllowSorting="True">

2.6.4 Les contrleurs


Le contrleur [global.asax, global.asax.vb] est le suivant : Composants serveur - 2 102/192

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

[global.asax.vb]
Imports st.istia.univangers.fr Imports System.Configuration Public Class Global Inherits System.Web.HttpApplication Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs) ' on cre un objet produits Dim objProduits As produits Try objProduits = New produits(ConfigurationSettings.AppSettings("OLEDBStringConnection")) ' on met l'objet dans l'application Application("objProduits") = objProduits ' pas d'erreur Application("erreur") = False Catch ex As Exception 'il y a eu erreur, on le note dans l'application Application("erreur") = True Application("message") = ex.Message End Try End Sub End Class

Lorsque l'application dmarre (Application_Start), nous construisons un objet [produits] et le mettons dans l'application afin qu'il soit disponible pour toutes les requtes de tous les clients. S'il y a exception lors de cette construction, elle est note dans l'application. La procdure [Application_Start] ne s'excutera qu'une fois. Ensuite le contrleur [global.asax] n'interviendra plus. C'est le contrleur [main.aspx.vb] qui fera alors le travail :
Imports Imports Imports Imports Imports Imports System.Collections Microsoft.VisualBasic System.Data st.istia.univangers.fr System System.Xml

Public Class main Inherits System.Web.UI.Page ' composants 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 btnExcuter 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 lnkRsultats As System.Web.UI.WebControls.LinkButton Protected WithEvents vueRsultats 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 rdDcroissant As System.Web.UI.WebControls.RadioButton ' donnes page Protected erreursHTML As String Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ' on regarde si l'application est en erreur If CType(Application("erreur"), Boolean) Then ' l'application ne s'est pas initialise correctement Dim erreurs As New ArrayList erreurs.Add("Application momentanment indisponible (" + CType(Application("message"), String) + ")") afficheErreurs(erreurs, False) Exit Sub End If '1re requte If Not IsPostBack Then ' on affiche le formulaire vide afficheFormulaire()

Composants serveur - 2

103/192

End If End Sub Private Sub afficheErreurs(ByVal erreurs As ArrayList, ByVal afficheLien As Boolean) ' affiche la vue erreurs erreursHTML = "" For i As Integer = 0 To erreurs.Count - 1 erreursHTML += "<li>" + erreurs(i).ToString + "</li>" + ControlChars.CrLf Next lnkErreurs.Visible = afficheLien ' on affiche la vue [erreurs] vueErreurs.Visible = True vueFormulaire.Visible = False vueRsultats.Visible = False End Sub Private Sub afficheFormulaire() ' on affiche la vue [formulaire] vueFormulaire.Visible = True vueErreurs.Visible = False vueRsultats.Visible = False End Sub Private Sub afficheRsultats(ByVal sqlTexte As String, ByVal donnes As DataView) ' on initialise les contrles lblSelect.Text = sqlTexte With DataGrid1 .DataSource = donnes .PageSize = CType(txtPages.Text, Integer) .CurrentPageIndex = 0 .DataBind() End With ' on affiche la vue [rsultats] vueRsultats.Visible = True vueFormulaire.Visible = False vueErreurs.Visible = False End Sub Private Sub btnExcuter_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnExcuter.Click ' page valide ? If Not Page.IsValid Then afficheFormulaire() Exit Sub End If ' excution de la requte SELECT client Dim donnes As DataView Try donnes = CType(Application("objProduits"), produits).getDataSet(txtSelect.Text.Trim).Tables(0).DefaultView Catch ex As Exception Dim erreurs As New ArrayList erreurs.Add("erreur d'accs la base de donnes (" + ex.Message + ")") afficheErreurs(erreurs, True) Exit Sub End Try ' tout va bien - on affiche les rsultats afficheRsultats(txtSelect.Text.Trim, donnes) ' on met les donnes dans la session Session("donnes") = donnes End Sub Private Sub retourFormulaire(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkErreurs.Click, lnkRsultats.Click ' jeu de vues vueErreurs.Visible = False vueFormulaire.Visible = True vueRsultats.Visible = False End Sub Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) Handles DataGrid1.PageIndexChanged ' chgt de page With DataGrid1 .CurrentPageIndex = e.NewPageIndex .DataSource = CType(Session("donnes"), DataView) .DataBind() End With End Sub

Composants serveur - 2

104/192

Private Sub DataGrid1_SortCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles DataGrid1.SortCommand ' on trie le dataview Dim donnes As DataView = CType(Session("donnes"), DataView) donnes.Sort = e.SortExpression + " " + CType(IIf(rdCroissant.Checked, "asc", "desc"), String) ' on l'affiche With DataGrid1 .CurrentPageIndex = 0 .DataSource = donnes .DataBind() End With End Sub End Class

Au chargement de la page [Page_Load], nous regardons tout d'abord si l'application a pu s'initialiser correctement. Si ce n'est pas le cas, on affiche la vue [erreurs] sans lien de retour vers le formulaire car ce lien est alors inutile. En effet, seule la vue [erreurs] peut s'afficher si l'application n'a pas pu s'initialiser correctement. Sinon, on affiche la vue [formulaire] si on a affaire la premire requte du client. Pour le reste, nous laissons le lecteur le soin de comprendre le code. Nous nous attardons seulement sur trois procdures, la procdure [btnExcuter_Click] qui s'excute lorsque l'utilisateur a demand l'excution de la requte SQL saisie dans la vue [formulaire], la procdure [DataGrid1_PageIndexChanged] qui est excute lorsque l'utilisateur utilise les liens [Suivant] et [Prcdent] du [DataGrid] et la procdure [DataGrid1_SortCommand] qui est excute lorsque l'utilisateur clique sur le titre d'une colonne pour trier les donnes selon l'ordre de celle-ci. Le sens de l'ordre, croissant ou dcroissant est fix par les deux boutons radio de tri. Dans la procdure [btnExcuter_Click], on commence donc par vrifier si la page est valide ou non. Lorsque la procdure [btnExcuter_Click] s'excute, les vrifications lies aux diffrents contrles de validation de la page ont t faites. Pour chaque contrle de validation, deux attributs ont t positionns :
IsValid ErrorMessage

vrai si la donne vrifie est valide, faux sionon le message d'erreur si la donne vrifie s'est avre invalide

Pour la page elle-mme, un attribut [IsValid] a t positionn. Il est vrai uniquement si tous les contrles de validation ont leur attribut [IsValid] vrai. Si ce n'est pas le cas, il faut afficher la vue [formulaire]. Celle-ci contient les contrles de validation qui afficheront leur attribut [errorMessage]. Si la page est valide , on utilise l'objet de type [produits] cr par [Application_Start] afin d'obtenir le [DataSet] correspondant l'excution de la requte SQL select. On transforme celui-ci en objet [DataView] :
Dim donnes As DataView Try donnes = CType(Application("objProduits"), produits).getDataSet(txtSelect.Text.Trim).Tables(0).DefaultView ...

On aurait pu travailler simplement avec le [DataSet] et crire :


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

Un objet [DataSet] est la base un ensemble de tables lies par des relations. Dans notre application particulire, le [DataSet] obtenu de la classe [produits] ne contient qu'une table, celle issue du rsultat de l'instruction [select]. Une table peut tre trie alors qu'un [DataSet] ne le peut pas, or on est intress par trier les donnes obtenues. Pour travailler avec la table rsultat du [select], on aurait pu crire :
Dim donnes As DataTable Try donnes = CType(Application("objProduits"), produits).getDataSet(txtSelect.Text.Trim).Tables(0) ...

L'objet [DataTable], bien que rprsentant une table de base de donnes n'a pas de mthode de tri. Il faut pour cela avoir une vue sur la table. Une vue est un objet de type [DataView]. On peut avoir diffrentes vues sur une mme table l'aide de filtres. Une table a une vue par dfaut qui est celle o aucun filtre n'est dfini. Elle reprsente donc la totalit de la table. Cette vue par dfaut est obtenue par [DataTable.DefaultView]. On peut trier une vue l'aide de sa proprit [sort] sur laquelle nous reviendrons. Si l'obtention du [DataSet] de la classe [produits] se passe bien, la vue [rsultats] est affiche sinon c'est la vue [erreurs]. L'affichage de la vue [rsultats] se fait par la procdure [afficheRsultats] qui on passe deux paramtres : le texte placer dans le label [lblSelect] le [DataView] lier [DataGrid1] Composants serveur - 2 105/192

Cet exemple nous montre la grande souplesse du composant [DataGrid]. Il sait reconnatre la structure du [DataView] auquel on le lie et s'adapter celle-ci. Enfin, la procdure [btnExcuter_Click] mmorise le [DataView] qu'elle vient d'obtenir dans la session de l'utilisateur afin d'en disposer lorsque celui-ci va demander d'autres pages du mme [DataView]. La procdure [DataGrid1_PageIndexChanged] est excute lorsque l'utilisateur utilise les liens [Suivant] et [Prcdent] du [DataGrid]. Elle reoit deux paramtres :
Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.) Handles DataGrid1.PageIndexChanged source e

l'objet origine de l'vnement - ici, l'un des liens [Suivant] ou [Prcdent] des informations sur l'vnement. L'attribut e.NewPageIndex est le n de page afficher pour rpondre la demande du client

Le code complet du gestionnaire d'vnement est le suivant :


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

Le composant [DataGrid] a un attribut [CurrentPageIndex] qui indique le n de la page qu'il affiche ou va afficher. On donne cet attribut la valeur [NewPageIndex] du paramtre [e]. Le [DataGrid] est ensuite associ au [DataView] qui avait t mmoris dans la session, par la procdure [btnExcuter_Click]. On peut se demander si le [DataGrid] a besoin de l'attribut [EnableViewState=true] puisque son contenu est calcul par le code chaque nouvelle excution de la page. On pouvait penser que non. Or, si le [DataGrid] a l'attribut [EnableViewState=false], on constate que l'vnement [DataGrid1.PageIndexChanged] n'est jamais dclench. C'est pour cela qu'on a laiss [EnableViewState=true]. On sait que cela entrane que le contenu du [DataGrid] va tre mis dans le champ cach [__VIEWSTATE] de la page. Cela peut entraner une surcharge importante de la page si le [DataGrid] est important. Si ce fait est gnant, on peut alors grer la pagination soi-mme sans utliliser la pagination automatique du [DataGrid]. La procdure [DataGrid1_SortCommand] est excute lorsque l'utilisateur clique sur le titre d'une des colonnes affiches par le [DataGrid] pour demander le tri des donnes dans l'ordre de cette colonne. Elle reoit deux paramtres :
Private Sub DataGrid1_SortCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles DataGrid1.SortCommand

source e

l'objet origine de l'vnement - ici, l'un des liens [Suivant] ou [Prcdent] des informations sur l'vnement. L'attribut [e.SortExpression] est le nom de la colonne clique pour le tri

Le code complet du gestionnaire d'vnement est le suivant :


Private Sub DataGrid1_SortCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles DataGrid1.SortCommand ' on trie le dataview Dim donnes As DataView = CType(Session("donnes"), DataView) donnes.Sort = e.SortExpression + " " + CType(IIf(rdCroissant.Checked, "asc", "desc"), String) ' on l'affiche With DataGrid1 .CurrentPageIndex = 0 .DataSource = donnes .DataBind() End With End Sub

On rcupre le [DataView] visualis par le [DataGrid] dans la session courante. C'est la procdure [btnExcuter_Click] qui l'y avait mis. Le composant [DataView] a une proprit [Sort] qui on attribue l'expression de tri. Celle-ci obit la syntaxe [select ... order by expr1, expr2, ...] o chaque [expri] peut tre suivi du mot cl [asc] pour un tri croissant ou [desc] pour un tri dcroissant. L'expression [order by] utilise ici, est [order by colonne asc/desc]. La proprit [e.SortExpression] nous donne le nom de la colonne du [DataGrid] qui a t clique pour le tri. La chane [asc/desc] est fixe d'aprs les valeurs des boutons radio du groupe [rdTri]. Une fois fixe l'expression de tri du [DataView], le [DataGrid] est associ celui-ci. On positionne le [DataGrid] sur sa premire page. Composants serveur - 2 106/192

2.7 Composant DataList et liaison de donnes


Nous nous intressons maintenant au composant [DataList]. Il offre plus de possibilits de mise en forme que le [DataGrid] mais est moins souple. Ainsi, il ne sait pas s'adapter automatiquement la source de donnes laquelle il est li. Il faut faire cette adaptation par code si celle-ci est souhaite. Si la structure de la source de donnes est connue d'avance, alors ce composant offre des possibilits de mise en forme qui peuvent le faire prfrer au [DataGrid].

2.7.1 Application
Afin d'illustrer l'utilisation du [DataList], nous construisons une application MVC analogue la prcdente : Client Interface client Logique applicative [global.asax], [main.aspx] CONTRLEUR Classe d'accs aux donnes [produits]

Donnes Sources de donnes

VUES

[rsultats1] [erreurs]

[rsultats2]

Les trois vues seront incorpores dans le code de prsentation du contrleur [main.aspx] sous la forme de conteneurs. Aussi cette application a-t-elle une unique page [main.aspx].

2.7.2 Les classes mtier


La classe [produits] est la mme que prcdemment.

2.7.3 Les vues


Lorsque l'utilisateur fait sa premire requte l'application, il obtient la vue [rsultats1] suivante :

1 2

n 1 2 3

nom
RadioButton1 RadioButton2 btnChanger DataList1

type RadioButton Button DataList

proprits EnableViewState=false EnableViewState=false EnableViewState=true

rle permet de choisir un style de [DataList] parmi deux bouton [submit] champ d'affichage de la liste de donnes

Si l'utilisateur choisit le style n 2, il obtient la vue [rsultats2] suivante :

Composants serveur - 2

107/192

n 1

nom
DataList2

type DataList

proprits EnableViewState=true

rle champ d'affichage de la liste de donnes

La vue [erreurs] signale un problme d'accs la source de donnes :

n 1

nom
erreursHTML

type variable

proprits

rle code HTML ncessaire l'affichage des erreurs

Les trois vues de l'application sont trois conteneurs (panel) diffrents au sein de la mme page [main.aspx]. Son code de prsentation est le suivant :
<%@ Page src="main.aspx.vb" inherits="main" autoeventwireup="false" Language="vb" %> <HTML> <HEAD> </HEAD> <body> <P>Liaison de donnes 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>

Composants serveur - 2

108/192

<asp:Panel id="vueRsultats1" 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="vueRsultats2" 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>

2.7.4 Configuration des composants [DataList]


Intressons-nous aux diffrents attributs d'un composant [DataList]. Ils sont trs nombreux et nous n'en prsentons qu'une faible partie. On peut dfinir jusqu' sept modles d'affichage l'intrieur d'un [DataList] :
HeaderTemplate ItemTemplate AlternatingItemTemplate SelectedItemTemplate SeparatorTemplate EditItemTemplate FooterTemplate

modle de l'entte du [DataList] modle des lignes affichant les lments de la liste de donnes associe. Seul ce modle est obligatoire. pour diffrentier visuellement les lments successifs affichs, on peut utiliser deux modles : ItemTemplate pour l'lment n, AlternatingItemTemplate pour l'lment n+1 modle de l'lment slectionn dans le [DataList] modle du sparateur entre deux lments du [DataList] un [DataList] rend possible la modification des valeurs qu'il affiche. [EditItemTemplate] est le modle d'un lment du [DataList] pour lequel on est en mode "dition" modle du pied de page du [DataList]

Le composant [DataList1] a t construit avec [WebMatrix]. Dans sa fentre de proprits on a slectionn le lien [Mise en forme automatique] :

Composants serveur - 2

109/192

1 2 3 4 5 6 7

Ci-dessus, le schma [Couleur 5] va gnrer un [DataList] avec des styles pour les modles suivants : HeaderTemplate (1), ItemTemplate (2, 6), AlternatingTemplate (3, 5), SelectedItemTemplate (4), FooterTemplate (7). Le code gnr est le suivant :
<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>

Aucun modle n'est dfini. C'est nous de le faire. Nous dfinissons les modles suivants :
HeaderTemplate <HeaderTemplate> Contenu de la table [liste] de la base [produits] </HeaderTemplate> <ItemTemplate> nom : <%# Container.DataItem("nom") %> <br> prix : <%# DataBinder.Eval(Container.DataItem,"prix","{0:C}") %> <br> </ItemTemplate>

ItemTemplate

Rappelons que le modle [ItemTemplate] est celui de l'affichage des lments de la source de donnes lie au [DataList]. Cette source de donnes est un ensemble de lignes de donnes, chacune comportant une ou plusieurs valeurs. La ligne courante de la source de donnes est reprsente par l'objet [Container.DataItem]. Une telle ligne a des colonnes. [Container.DataItem("col1")] est la valeur de la colonne "col1" de la ligne courante. Pour inclure cette valeur dans le code de prsentation, on crit <%# Container.DataItem("col") %>. Parfois, on veut prsenter un lment de la ligne courante sous un format spcial. Ici, on veut afficher la colonne "prix" de la ligne courante en euros. On utilise alors la fonction [DataBinder.Eval] qui adamet trois paramtres : la ligne courante [Container.DataItem] le nom de la colonne formater la chane de formatage sous la forme {0:format} o [format] est l'un des formats accepts par la mthode [string.format]. Ainsi le code <%# DataBinder.Eval(Container.DataItem,"prix",{0:C}) %> va faire afficher la colonne [prix] de la ligne courante sous forme montaire (format C=Currency). Nous aurons donc un [DataList] qui va ressembler ceci :

Ci-dessus, les donnes ont t places raison de quatre donnes par ligne. Cela est obtenu avec les attributs suivants de [DataList] : Composants serveur - 2 110/192

RepeatDirection RepeatColumns

Horizontal nombre de colonnes dsires

Au final, le code de [DataList1] est celui qui a t prsent dans le code de prsentation un peu plus haut. Nous laissons au lecteur le soin d'tudier le code de prsentation de [DataList2]. Comme pour le composant [DataGrid], la plupart des proprits de [DataList] peuvent tre fixes l'aide d'un assistant de [WebMatrix]. On utilise pour cela, le lien [Gnrateur de proprits] de la fentre de proprits du [DataList] :

2.7.5 Les contrleurs


Le contrleur [global.asax, global.asax.vb] est le suivant : [global.asax]
<%@ Application src="global.asax.vb" inherits="Global" %>

[global.asax.vb]
Imports Imports Imports Imports Imports Imports Imports System System.Web System.Web.SessionState st.istia.univangers.fr System.Configuration System.Data System.Collections

Public Class Global Inherits System.Web.HttpApplication Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs) ' on cre un objet produits Try Dim donnes As DataSet = New produits(ConfigurationSettings.AppSettings("OLEDBStringConnection")).getDataSet("select * from LISTE") ' on met l'objet dans l'application Application("donnes") = donnes ' pas d'erreur Application("erreur") = False Catch ex As Exception 'il y a eu erreur, on le note dans l'application Application("erreur") = True Application("message") = ex.Message End Try End Sub End Class

Composants serveur - 2

111/192

Lorsque l'application dmarre (Application_Start), nous construisons un [dataset] partir de la classe mtier [produits] et le mettons dans l'application afin qu'il soit disponible pour toutes les requtes de tous les clients. S'il y a exception lors de cette construction, elle est note dans l'application. La procdure [Application_Start] ne s'excutera qu'une fois. Ensuite le contrleur [global.asax] n'interviendra plus. C'est le contrleur [main.aspx.vb] qui fera alors le travail :
Imports Imports Imports Imports Imports Imports System.Collections Microsoft.VisualBasic System.Data st.istia.univangers.fr System System.Xml

Public Class main Inherits System.Web.UI.Page ' composants 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 vueRsultats1 As System.Web.UI.WebControls.Panel Protected WithEvents vueRsultats2 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 ' donnes page Protected erreursHTML As String Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ' on regarde si l'application est en erreur If CType(Application("erreur"), Boolean) Then ' l'application ne s'est pas initialise correctement Dim erreurs As New ArrayList erreurs.Add("Application momentanment indisponible (" + CType(Application("message"), String) ")") afficheErreurs(erreurs, False) Exit Sub End If '1re requte If Not IsPostBack Then ' on initialise les contrles With DataList1 .DataSource = CType(Application("donnes"), DataSet) .DataBind() End With With DataList2 .DataSource = CType(Application("donnes"), DataSet) .DataBind() End With ' on affiche le formulaire vide afficheRsultats(True, False) End If End Sub Private Sub afficheErreurs(ByVal erreurs As ArrayList, ByVal afficheLien As Boolean) ' affiche la vue erreurs erreursHTML = "" For i As Integer = 0 To erreurs.Count - 1 erreursHTML += "<li>" + erreurs(i).ToString + "</li>" + ControlChars.CrLf Next ' on affiche la vue [erreurs] vueErreurs.Visible = True vueRsultats1.Visible = False vueRsultats2.Visible = False bandeau.Visible = False End Sub Private Sub afficheRsultats(ByVal visible1 As Boolean, ByVal visible2 As Boolean) ' on affiche la vue [rsultats] vueRsultats1.Visible = visible1 vueRsultats2.Visible = visible2 vueErreurs.Visible = False End Sub Private Sub btnChanger_Click(ByVal sender As System.Object, btnChanger.Click ' on change le style afficheRsultats(RadioButton1.Checked, RadioButton2.Checked) End Sub End Class ByVal e As System.EventArgs) Handles

Composants serveur - 2

112/192

2.8 Composant Repeater et liaison de donnes


Le composant [Repeater] permet de rpter le code HTML de chacun des lments d'une liste de donnes. Supposons qu'on veuille afficher une liste d'erreurs sous la forme suivante :

Nous avons dj rencontr ce problme et nous l'avons rsolu en mettant dans le code de prsentation une variable sous la forme <ul><% =erreursHTML %></ul>, la valeur de erreursHTML tant calcule par le contrleur. Cette valeur contient du code HTML, celle d'une liste. Cela prsente l'inconvnient que si on veut modifier la prsentation de cette liste HTML on est oblig d'aller dans la partie contrleur, ce qui va l'encontre la sparation contrleur/prsentation. Le composant [Repeater] nous apporte une solution. Comme pour le [DataList], on peut dfinir les modles <HeaderTemplate> pour l'entte, <ItemTemplate> pour l'lment courant de la liste de donnes et <FooterTemplate> pour la fin des donnes. Ici, nous pourrions avoir la dfinition suivante pour le composant [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>

On rappelle que [Container.DataItem] reprsente une ligne de donnes si la source de donnes a plusieurs colonnes. Elle reprsente une donne si la source n'a qu'une colonne. Ce sera le cas ici. Pour exemple, nous construisons l'application suivante :

Le code de prsentation de la page est le suivant :


<html> <head> </head> <body> <form runat="server"> <p> Liaison de donnes 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>

Composants serveur - 2

113/192

</HeaderTemplate> <FooterTemplate> </ul> </FooterTemplate> </asp:Repeater> </p> </form> </body> </html>

Le code de contrle est le suivant :


<%@ Page Language="VB" %> <script runat="server"> ' procdure excute au chargement de la page Sub page_Load(sender As Object, e As EventArgs) if not IsPostBack then ' on cre une source de donnes with Repeater1 .DataSource=createDataSource .DataBind end with end if End Sub function createDataSource as ArrayList ' cre un 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>

A la premire requte du client, nous associons un objet [ArrayList] au composant [Repeater] suppos reprsenter une liste d'erreurs.

2.9 Application
Nous reprenons ici, une application dj traite avec des composants serveur. L'application permet de faire des simulations de calculs d'impt. Elle s'appuie sur une classe [impot] qu'on ne rappellera pas ici. Cette classe a besoin de donnes qu'elle trouve dans une source de donnes OLEDB. Pour l'exemple, ce sera une source ACCESS. Nous amenons les nouveauts suivantes dans cette nouvelle version : l'usage de composants de validation pour vrifier la validit des donnes l'usage de composants serveur lis des sources de donnes pour l'affichage des rsultats

2.9.1 La structure MVC de l'application


La structure MVC de l'application est la suivante :

Composants serveur - 2

114/192

Client Interface client

Logique applicative [main.aspx] CONTRLEUR Classe mtier [impot] Classe d'accs donnes [impotsOLEDB]

Donnes Sources de donnes

VUES

[formulaire] [erreurs]

[simulations]

Les trois vues seront incorpores dans le code de prsentation du contrleur [main.aspx] sous la forme de conteneurs. Aussi cette application a-t-elle une unique page [main.aspx].

2.9.2 Les vues de l'application


La vue [formulaire] est le formulaire de saisie des informations permettant le calcul de l'impt d'un utilisateur :

L'utilisateur remplit le formulaire :

Il utilise le bouton [Envoyer] pour demander le calcul de son impt. Il obtient la vue [simulations] suivante :

Il retourne au formulaire par le lien ci-dessus. Il le retrouve dans l'tat o il l'a saisi. Il peut faire des erreurs de saisie : Composants serveur - 2 115/192

Celles-ci lui sont signales sur la vue [formulaire] :

L'utilise corrige alors ses erreurs. Il peut faire de nouvelles simulations :

Il obtient alors la vue [simulations] avec une simulation de plus :

Enfin, si la source de donnes n'est pas disponible, cela est signal l'utilisateur dans la vue [erreurs] :

Composants serveur - 2

116/192

2.9.2.1 Le code de prsentation


Rappelons que la page [main.aspx] rassemble toutes les vues. C'et un unique formulaire avec trois conteneurs : [panelform] pour la vue [formulaire] [panelerreurs] pour la vue [erreurs] [panelsimulations] pour la vue [simulations] Nous dtaillons maintenant les composants de ces trois conteneurs. Le conteneur [panelform] a la reprsentation visuelle suivante : 0 1 3 6 9 10 7,8

2 4,5

n
0 1 2

nom
panelform rdOui rdNon cvMarie

type Panel RadioButton CustomValidator

proprits GroupName=rdmarie vue formulaire boutons radio

rle

3 4

txtEnfants rfvEnfants

rvEnfants

6 7

txtSalaire rfvSalaire

revSalaire

9 10

btnCalculer btnEffacer

ErrorMessage=Vous n'avez vrifie que le programme client a bien envoy le statut pas indiqu votre tat marital marital attendu EnableClientScript=false TextBox nombre d'enfants RequiredFieldValidator ErrorMessage=Indiquez le vrifie que le champ [txtEnfants] n'est pas vide nombre d'enfants ControlToValidate=txtEnfants RangeValidator ErrorMessage=Tapez un vrifie que le champ [txtEnfants] est dans l'intervalle nombre entre 1 et 30 [1,30] ControlToValidate=txtEnfants TextBox EnableViewState=true salaire annuel RequiredFieldValidator ControlToValidate=txtSalaire vrifie que le champ [txtSalaire] n'est pas vide ErrorMessage=Indiquez le montant de votre salaire RegularExpressionValidator ControlToValidate=txtSalaire vrifie que le champ [txtSalaire] est une suite de ErrorMessage=Salaire invalide chiffes RegularExpression=\s*\d+\s* Button CausesValidation=true bouton [submit] du formulaire - lance le calcul de l'impt Button CausesValidation=false bouton [submit] du formulaire - vide le formulaire

On pourra s'tonner du contrle [cvMari] charg de vrifier que l'utilisateur a bien coch l'un des deux boutons radio. Il ne peut en effet en tre autrement s'il utilise bien le formulaire envoy par le serveur. Comme rien ne peut l'assurer, on est obligs de vrifier tous les paramtres posts. On notera galement l'attribut [CausesValidation=false] du bouton [btnEffacer]. En effet, lorsque l'utilisateur utilise ce bouton on ne doit pas vrifier les donnes postes puisqu'elles vont tre ignores. Tous les composants du conteneur ont la propit [EnableViewState=false] sauf [panelForm, txtEnfants, txtSalaire, rdOui, rdNon]. Le conteneur [panelerreurs] a la reprssentation visuelle suivante : 0 1

n
0

nom
panelerreurs

type Panel

proprits EnableViewState=false

rle vue erreurs 117/192

Composants serveur - 2

rptErreurs

Repeater

EnableViewState=false

affiche une liste d'erreurs

Le composant [rptErreurs] est dfini de la faon suivante :


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

La liste de donnes associe au composant [rptErreurs] sera un objet [ArrayList] contenant une liste de messages d'erreurs. Ainsi cidessus, <%# Container.Dataitem%> dsigne le message d'erreur courant. Le conteneur [panelsimulations] a la reprsentation visuelle suivante : 0

n
0 2 1

nom
panelsimulations lnkForm2 dgSimulations

type Panel LinkButton DataGrid

proprits EnableViewState=false EnableViewState=false EnableViewState=false

rle vue simulations lien vers le formulaire charg d'afficher les simulations places dans un objet [DataTable]

Le composant [dgSimulations] est configur de la faon suivante sous [Webmatrix]. Dans la fentre de proprits de [dgSimulations], nous slectionnons le lien [Mise en forme automatique] et slectionnons le schma [Couleur 3] :

Puis, toujours dans la fentre de proprits de [dlgSimulations], nous slectionnons le lien [Gnrateur de proprits]. Nous demandons l'affichage de l'en-tte des colonnes :

Composants serveur - 2

118/192

Nous slectionnons l'option [Colonnes] pour dfinir les quatre colonnes du [DataGrid] :

Nous dcochons la phrase [Crer des colonnes automatiquement ...]. Nous allons dfinir nous-mmes les quatre colonnes du [DataGrid]. Celui-ci sera associ un objet [DataTable] quatre colonnes nommes respectivement "mari", "enfants", "salaire", "impt". Pour crer une colonne dans le [DataGrid], nous slectionnons l'option [Colonnes connexe] et utilisons le bouton de cration comme indiqu ci-dessus. Une colonne est cre et nous pouvons dfinir ses caractristiques. La premire colonne du tableau des simulations contiendra la colonne "mari" de l'objet [DataTable] qui sera associ au [DataGrid]. Cette premire colonne du [DataGrid] est dfinie comme suit dans l'assistant :

Texte de l'en-tte Champ de donnes

titre de la colonne, ici "Mari" nom de la colonne de la source de donnes qui sera visualise par cette colonne du [DataGrid]. Ici, c'est la colonne "mari" du [DataTable].

La deuxime colonne est dfinie comme suit :


Texte de l'en-tte Champ de donnes

Enfants enfants

La troisme colonne est dfinie comme suit :


Texte de l'en-tte Champ de donnes Expression de mise en forme

Salaire annuel salaire {0:C} - affichage montaire pour obtenir le signe euro 119/192

Composants serveur - 2

La quatrime colonne est dfinie comme suit :


Texte de l'en-tte Champ de donnes Expression de mise en forme

Montant de l'impt impt {0:C} - affichage montaire pour obtenir le signe euro

Nous mettons le code de de prsentation et le code de contrle dans deux fichiers spars. Le premier sera dans [main.aspx] et le second dans [main.aspx.vb]. Le code de [main.aspx] est le suivant :
<%@ page src="main.aspx.vb" inherits="main" AutoEventWireUp="false" %> <HTML> <HEAD> <title>Calculer votre impt</title> </HEAD> <body> <P>Calculer votre impt</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>

Composants serveur - 2

120/192

</asp:Repeater> </asp:panel> <asp:panel id="panelsimulations" runat="server" EnableViewState="False"> <P> <asp:DataGrid id="dgSimulations" runat="server" BorderColor="#DEBA84" BorderStyle="None" CellSpacing="2" BorderWidth="1px" BackColor="#DEBA84" CellPadding="3" AutoGenerateColumns="False" EnableViewState="False"> <SelectedItemStyle Font-Bold="True" ForeColor="White" BackColor="#738A9C"></SelectedItemStyle> <ItemStyle ForeColor="#8C4510" BackColor="#FFF7E7"></ItemStyle> <HeaderStyle Font-Bold="True" ForeColor="White" BackColor="#A55129"></HeaderStyle> <FooterStyle ForeColor="#8C4510" BackColor="#F7DFB5"></FooterStyle> <Columns> <asp:BoundColumn DataField="mari&#233;" HeaderText="Mari&#233;"></asp:BoundColumn> <asp:BoundColumn DataField="enfants" HeaderText="Enfants"></asp:BoundColumn> <asp:BoundColumn DataField="salaire" HeaderText="Salaire annuel" DataFormatString="{0:C}"></asp:BoundColumn> <asp:BoundColumn DataField="imp&#244;t" HeaderText="Montant de l'imp&#244;t" DataFormatString="{0:C}"></asp:BoundColumn> </Columns> <PagerStyle HorizontalAlign="Center" ForeColor="#8C4510" Mode="NumericPages"></PagerStyle> </asp:DataGrid></P> <P> <asp:LinkButton id="lnkForm2" runat="server" EnableViewState="False">Retour au formulaire</asp:LinkButton></P> </asp:panel> </FORM> </body> </HTML>

2.9.3 Le code de contrle de l'application


Le code de contrle de l'application se trouve rparti dans les fichiers [global.asax.vb] et [main.aspx.vb]. Le fichier [global.asax] est dfini comme suit :
<%@ Application src="Global.asax.vb" Inherits="Global" %>

Le fichier [global.asax.vb] est le suivant :


Imports Imports Imports Imports Imports Imports Imports System System.Web System.Web.SessionState st.istia.univangers.fr System.Configuration System.Collections System.Data

Public Class Global Inherits System.Web.HttpApplication Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs) ' on cre un objet impot Dim objImpot As impot Try objImpot = New impot(New impotsOLEDB(ConfigurationSettings.AppSettings("chaineConnexion"))) ' on met l'objet dans l'application Application("objImpot") = objImpot ' pas d'erreur Application("erreur") = False Catch ex As Exception 'il y a eu erreur, on le note dans l'application Application("erreur") = True Application("message") = ex.Message End Try End Sub Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs) ' dbut de session - on cre une liste de simulations vides 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("impt", Type.GetType("System.Int32")) Session.Item("simulations") = simulations

Composants serveur - 2

121/192

End Sub End Class

Lorsque l'application dmarre (1re requte faite l'application), la procdure [Application_Start] est excute. Elle cherche crer un objet de type [impot] prenant ses donnes dans une source OLEDB. Le lecteur est invit lire le chapitre 5 o cette classe a t dfinie, s'il l'a oublie. La construction de l'objet [impot] peut chouer si la source de donnes n'est pas disponible. Dans ce cas, l'erreur est mmorise dans l'application afin que toutes les requtes ultrieures sachent que celle-ci n'a pu s'initialiser correctement. Si la construction se passe bien, l'objet [impot] cr est lui aussi mmoris dans l'application. Il sera utilis par toutes les requtes de calcul d'impt. Lorsqu'un client fait sa premire requte, une session est cre pour lui par la procdure [Application_Start]. Cette session est destine mmoriser les diffrentes simulations de calcul d'impt qu'il va faire. Celles-ci seront mmorises dans un objet [DataTable] associ la cl de session "simulations". Lorsque la session dmarre, cette cl est associe un objet [DataTable] vide mais dont la structure a t dfinie. Les informations ncessaires l'application sont places dans son fichier de configuration [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 cl [chaineConnexion] dsigne la chane de connexion la source OLEDB. L'autre partie du code de contrle se trouve dans [main.aspx.vb] :
Imports Imports Imports Imports Imports Imports System.Collections Microsoft.VisualBasic st.istia.univangers.fr System System.Web.UI.Control System.Data

Public Class main Inherits System.Web.UI.Page Protected Protected Protected Protected Protected Protected Protected Protected Protected Protected Protected Protected Protected Protected Protected Protected Protected WithEvents WithEvents WithEvents WithEvents WithEvents WithEvents WithEvents WithEvents WithEvents WithEvents WithEvents WithEvents WithEvents WithEvents WithEvents WithEvents WithEvents rdOui As System.Web.UI.WebControls.RadioButton rdNon As System.Web.UI.WebControls.RadioButton txtEnfants As System.Web.UI.WebControls.TextBox txtSalaire As System.Web.UI.WebControls.TextBox btnCalculer As System.Web.UI.WebControls.Button btnEffacer As System.Web.UI.WebControls.Button panelform As System.Web.UI.WebControls.Panel lnkForm2 As System.Web.UI.WebControls.LinkButton panelerreurs As System.Web.UI.WebControls.Panel rfvEnfants As System.Web.UI.WebControls.RequiredFieldValidator rvEnfants As System.Web.UI.WebControls.RangeValidator rfvSalaire As System.Web.UI.WebControls.RequiredFieldValidator rptErreurs As System.Web.UI.WebControls.Repeater dgSimulations As System.Web.UI.WebControls.DataGrid revSalaire As System.Web.UI.WebControls.RegularExpressionValidator cvMarie As System.Web.UI.WebControls.CustomValidator panelsimulations As System.Web.UI.WebControls.Panel

' variables locales 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)

Composants serveur - 2

122/192

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

Le premier vnement trait par le code est [Page_Load] :


Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ' tout d'abord, on regarde dans quel tat est l'application If CType(Application("erreur"), Boolean) Then ' l'application n'a pas pu s'initialiser ' on affiche la vue erreurs Dim erreurs As New ArrayList erreurs.Add("Application momentanment indisponible (" + CType(Application("message"), String) ")") afficheErreurs(erreurs) Exit Sub End If ' pas d'erreurs - la 1re requte, on prsente le formulaire If Not IsPostBack Then afficheFormulaire() End Sub

Avant de commencer traiter la requte, nous nous assurons que l'application a pu s'initialiser correctement. Si ce n'est pas le cas, on affiche la vue [erreurs] avec la procdure [afficheErreurs]. Si c'est la premire requte (IsPostBack=false), nous faisons afficher la vue [formulaire] avec [afficheFormulaire]. La procdure affichant la vue [erreurs] est la suivante :
Private Sub afficheErreurs(ByRef erreurs As ArrayList) ' construit la liste des erreurs With rptErreurs .DataSource = erreurs .DataBind() End With ' affichage des conteneurs panelerreurs.Visible = True panelform.Visible = False panelsimulations.Visible = False Exit Sub End Sub

La procdure reoit comme paramtre une liste de messages d'erreurs dans [erreurs] de type [ArrayList]. Nous nous contentons de lier cette source de donnes au composant [rptErreurs] charg de l'afficher. La procdure affichant la vue [formulaire] est la suivante :
Private Sub afficheFormulaire() ' affiche le formulaire panelform.Visible = True ' les autres conteneurs sont cachs panelerreurs.Visible = False panelsimulations.Visible = False End Sub

Cette procdure se contente de rendre visible le conteneur [panelform]. Les composants sont affichs avec leur valeur poste ou prcdente (VIEWSTATE).

Composants serveur - 2

123/192

Lorsque l'utilisateur clique sur le bouton [Calculer] de la vue [formulaire], un POST vers [main.aspx] est fait. La procdure [Page_Load] est excute puis la procdure [btnCalculer_Click] :
Private Sub btnCalculer_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) btnCalculer.Click ' on vrifie la validit des donnes saisies, s'il y a des erreurs, on le signale If Not Page.IsValid Then afficheFormulaire() Exit Sub End If ' pas d'erreurs - on calcule l'impt Dim impot As Long = CType(Application("objImpot"), impot).calculer( _ rdOui.Checked, CType(txtEnfants.Text, Integer), CType(txtSalaire.Text, Long)) ' on rajoute le rsultat aux simulations existantes 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("impt") = impot simulations.Rows.Add(simulation) ' on met les simulations dans la session Session.Item("simulations") = simulations ' on affiche la page des simulations afficheSimulations(simulations, "Retour au formulaire") End Sub Handles

La procdure commence par vrifier la validit de la page. Rappelons ici que lorsque la procdure [btnCalculer_Click] s'excute, les contrles de validation ont fait leur travail et l'attribut [IsValid] de la page a t positionn. Si la page n'est pas valide, alors la vue [formulaire] est affiche de nouveau et la procdure est termine. Les composants de validation de la vue [formulaire] ayant leur attribut [IsValid] [false] afficheront leur attribut [ErrorMessage]. Si la page est valide, alors le montant de l'impt est calcul grce l'objet de type [impot] qui avait t stock dans l'application au dmarrage de celle-ci. Cette nouvelle simulation est ajoute la liste des simulations dj faites et stocke dans la session. Enfin la vue [simulations] est affiche par la procdure [afficheSimulations] suivante :
Private Sub afficheSimulations(ByRef simulations As DataTable, ByRef lien As String) ' on lie le datagrid la source simulations With dgSimulations .DataSource = simulations .DataBind() End With ' lien lnkForm2.Text = lien ' les vues panelsimulations.Visible = True panelerreurs.Visible = False panelform.Visible = False End Sub

La procdure a deux paramtres : une liste de simulations dans [simulations] de type [DataTable] un texte de lien dans [lien] La source de donnes [simulations] est lie au composant charg de l'afficher. Le texte du lien est lui plac dans la proprit [Text] de l'objet [LinkButton] de la vue. Lorsque l'utilisateur clique sur le bouton [Effacer] de la vue [formulaire], c'est la procdure [btnEffacer_click] qui s'excute (toujours aprs [Page_Load]) :
Private Sub btnEffacer_Click(ByVal btnEffacer.Click ' affiche le formulaire vide razForm() afficheFormulaire() End Sub Private Sub razForm() ' vide le formulaire rdOui.Checked = False rdNon.Checked = True txtEnfants.Text = "" txtSalaire.Text = "" End Sub sender As System.Object, ByVal e As System.EventArgs) Handles

Composants serveur - 2

124/192

Le code ci-dessus est suffisamment simple pour ne pas avoir tre comment. Il nous reste grer le clic sur les liens des vues [erreurs] et [simulations] :
Private Sub lnkForm1_Click(ByVal lnkForm1.Click ' affiche le formulaire afficheFormulaire() End Sub Private Sub lnkForm2_Click(ByVal lnkForm2.Click ' affiche le formulaire afficheFormulaire() End Sub sender As System.Object, ByVal e As System.EventArgs) Handles

sender

As

System.Object,

ByVal

As

System.EventArgs)

Handles

Les deux procdures se contentent de faire afficher la vue [formulaire]. Nous savons que les champs de celle-ci vont obtenir une valeur qui est soit la valeur poste pour eux soit leur valeur prcdente. Comme ici, le POST du client n'envoie aucune valeur pour les champs du formulaire, ceux-ci vont retrouver leur valeur prcdente. Le formulaire est donc affich avec les valeurs saisies par l'utilisateur.

2.9.4 Tests
Tous les fichiers ncessaires l'application sont placs dans un Le dossier [bin] contient la dll contenant les classes [impot], dossier <application-path> : [impotsData], [impotsOLEDB] ncessaires l'application :

Le lecteur pourra, s'il le souhaite, relire le chapitre 5 o est explique la faon de crer le fichier [impot.dll] ci-dessus. Ceci fait, le serveur Cassini est lanc avec les paramtres (<application-path>,/impots6). On demande l'url [http://impots6/main.aspx] avec un navigateur :

Si on renomme le fichier ACCESS [impots.mdb] en [impots1.mdb], on aura la page suivante :

Composants serveur - 2

125/192

2.9.5 Conclusion
Nous avons une application MVC n'utilisant que des composants serveurs. L'usage de ceux-ci a permis de sparer compltement la partie prsentation de l'application de sa partie contrle. Cela n'avait pas encore t possible jusqu' maintenant o le code de prsentation contenait des variables calcules par le code de contrle et ayant une valeur contenant du code HTML. Maintenant ce n'est plus le cas.

Composants serveur - 2

126/192

3 Composants serveur ASP - 3


3.1 Introduction
Nous continuons notre travail sur l'interface utilisateur en approfondissant les capacits des composants [DataList], [DataGrid], notamment dans le domaine de la mise jour des donnes qu'ils affichent

3.2 Grer les vnements associs aux donnes des composants liaison de donnes
3.2.1 L'exemple
Considrons la page suivante :

La page comprend trois composants associs une liste de donnes : un composant [DataList] nomm [DataList1] un composant [DataGrid] nomm [DataGrid1] un composant [Repeater] nomm [Repeater1] La liste de donnes associe est le tableau {"zro","un","deux","trois"}. A chacune de ces donnes est associ un groupe de deux boutons libells [Infos1] et [Infos2]. L'utilisateur clique sur l'un des boutons et un texte affiche le nom du bouton cliqu. On cherche montrer ici comment grer une liste de boutons ou de liens.

3.2.2 La configuration des composants


Le composant [DataList1] est configur de la faon suivante :
<asp:datalist id="DataList1" ... runat="server"> <SelectedItemStyle ...</SelectedItemStyle> <HeaderTemplate> [dbut] </HeaderTemplate> <FooterTemplate> [fin]

Composants serveur - 2

127/192

</FooterTemplate> <ItemStyle ...></ItemStyle> <ItemTemplate> <P><%# Container.DataItem %> <asp:Button runat="server" Text="Infos1" CommandName="infos1"></asp:Button> <asp:Button runat="server" Text="Infos2" CommandName="infos2"></asp:Button></P> </ItemTemplate> <FooterStyle ...></FooterStyle> <HeaderStyle ...></HeaderStyle> </asp:datalist>

Nous avons omis tout ce qui correspondait l'apparence du [DataList] pour ne s'intresser qu' son contenu : la section <HeaderTemplate> dfinit l'en-tte du [DataList] et la section <FooterTemplate> son pied-de-page. la section <ItemTemplate> est le modle d'affichage utilis pour chacune des donnes de la liste de donnes associe. On y trouve les lments suivants : o la valeur de la donne courante de la iste des donnes associe au composant : <%# Container.DataItem %> o deux boutons libells respectivement [Infos1] et [Infos2]. La classe [Button] a un attribut [CommandName] qui est utilis ici. Il va nous permettre de dterminer quel est le bouton l'origine d'un vnement dans le [DataList]. Pour grer le clic sur les boutons, on n'aura qu'un seul gestionnaire d'vnement qui sera li au [DataList] luimme et non aux boutons. Ce gestionnaire recevra une information lui indiquant sur quelle ligne du [DataList] s'est produit le clic. L'attribut [CommandName] nous permettra de savoir sur quel bouton de la ligne il s'est produit. Le composant [Repeater1] est configur de faon trs analogue :
<asp:repeater id="Repeater1" runat="server"> <HeaderTemplate> [dbut]<hr /> </HeaderTemplate> <FooterTemplate> <hr /> [fin] </FooterTemplate> <SeparatorTemplate> <hr /> </SeparatorTemplate> <ItemTemplate> <%# Container.DataItem %> <asp:Button runat="server" Text="Infos1" CommandName="infos1"></asp:Button> <asp:Button runat="server" Text="Infos2" CommandName="infos2"></asp:Button></P> </ItemTemplate> </asp:repeater>

On a simplement ajout une section <SeparatorTemplate> pour que les donnes successives affiches par le composant soient spares par une barre horizontale. Enfin, le composant [DataGrid1] est configur comme suit :
<asp:datagrid id="DataGrid1" ... runat="server" PageSize="2" AllowPaging="True"> <SelectedItemStyle ...></SelectedItemStyle> <AlternatingItemStyle ...></AlternatingItemStyle> <ItemStyle ...></ItemStyle> <HeaderStyle ...></HeaderStyle> <FooterStyle ....></FooterStyle> <Columns> <asp:ButtonColumn Text="Infos1" ButtonType="PushButton" CommandName="Infos1"> </asp:ButtonColumn> <asp:ButtonColumn Text="Infos2" ButtonType="PushButton" CommandName="Infos2"> </asp:ButtonColumn> </Columns> <PagerStyle .... Mode="NumericPages"></PagerStyle> </asp:datagrid>

L galement, nous avons omis les informations de style (couleurs, largeurs, ...). Nous sommes en mode de gnration automatique des colonnes qui est le mode par dfaut du [DataGrid]. Cela signifie qu'il y aura autant de colonnes qu'il y en a dans la source de donnes. Ici, il y en a une. Nous avons ajout deux autres colonnes balises par <asp:ButtonColumn>. Nous y dfinissons des informations analogues celles dfinies pour les deux autres composants ainsi que le type de bouton, ici [PushButton]. Le type par dfaut est [LinkButton], c.a.d. un lien. Par ailleurs, les donnes seront pagines [AllowPaging=true] avec une taille de page de deux lments [PageSize=2].

Composants serveur - 2

128/192

3.2.3 Le code de prsentation de la page


Le code de prsentation de notre page exemple a t plac dans un fichier [main.aspx] :
<%@ page codebehind="main.aspx.vb" inherits="vs.main" autoeventwireup="false" %> <HTML> <HEAD> </HEAD> <body> <form runat="server"> <P>Gestion d'vnements de composants associs des listes de donnes</P> <HR width="100%" SIZE="1"> <table cellSpacing="1" cellPadding="1" bgColor="#ffcc00" border="1"> <tr> <td ...>DataList</td> <td ...>DataGrid</td> <td ...>Repeater</td> </tr> <tr> <td ...> <asp:datalist id="DataList1" ... runat="server"> <HeaderTemplate> [dbut] </HeaderTemplate> <FooterTemplate> [fin] </FooterTemplate> <ItemStyle ...></ItemStyle> <ItemTemplate> <P><%# Container.DataItem %> <asp:Button runat="server" Text="Infos1" CommandName="infos1"></asp:Button> <asp:Button runat="server" Text="Infos2" CommandName="infos2"></asp:Button></P> </ItemTemplate> <FooterStyle ...></FooterStyle> <HeaderStyle ....></HeaderStyle> </asp:datalist> <P></P> </td> <td ...> <asp:datagrid id="DataGrid1" ... runat="server" PageSize="2" AllowPaging="True"> <AlternatingItemStyle ...></AlternatingItemStyle> <ItemStyle ...></ItemStyle> <HeaderStyle ...></HeaderStyle> <FooterStyle ...></FooterStyle> <Columns> <asp:ButtonColumn Text="Infos1" ButtonType="PushButton" CommandName="Infos1"> </asp:ButtonColumn> <asp:ButtonColumn Text="Infos2" ButtonType="PushButton" CommandName="Infos2"> </asp:ButtonColumn> </Columns> <PagerStyle ... Mode="NumericPages"></PagerStyle> </asp:datagrid> </td> <td ...> <asp:repeater id="Repeater1" runat="server"> <HeaderTemplate> [dbut]<hr /> </HeaderTemplate> <FooterTemplate> <hr /> [fin] </FooterTemplate> <SeparatorTemplate> <hr /> </SeparatorTemplate> <ItemTemplate> <%# Container.DataItem %> <asp:Button runat="server" Text="Infos1" CommandName="infos1"></asp:Button> <asp:Button runat="server" Text="Infos2" CommandName="infos2"></asp:Button></P> </ItemTemplate> </asp:repeater> </td>

Composants serveur - 2

129/192

</tr> </table> <P><asp:label id="lblInfo" runat="server"></asp:label></P> <P></P> </form> </body> </HTML>

Dans le code ci-dessus, nous avons omis le code de mise en forme (couleurs, lignes, tailles, ...)

3.2.4 Le code de contrle de la page


La code de contrle de l'application a t plac dans le fichier [main.aspx.vb] : Public
Class main Inherits System.Web.UI.Page

' composants page Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents

DataList1 As System.Web.UI.WebControls.DataList lblInfo As System.Web.UI.WebControls.Label DataGrid1 As System.Web.UI.WebControls.DataGrid Repeater1 As System.Web.UI.WebControls.Repeater

' la source de donnes Protected textes() As String = {"zro", "un", "deux", "trois"} Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load If Not IsPostBack Then 'liaisons avec source de donnes DataList1.DataSource = textes DataGrid1.DataSource = textes Repeater1.DataSource = textes Page.DataBind() End If End Sub Private Sub DataList1_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataListCommandEventArgs) Handles DataList1.ItemCommand ' un vt s'est produit sur une des lignes du [datalist] lblInfo.Text = "Vous avez cliqu sur le bouton [" + e.CommandName + "] de l'lment [" + e.Item.ItemIndex.ToString + "] du composant [DataList]" End Sub Private Sub Repeater1_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.RepeaterCommandEventArgs) Handles Repeater1.ItemCommand ' un vt s'est produit sur une des lignes du [repeater] lblInfo.Text = "Vous avez cliqu sur le bouton [" + e.CommandName + "] de l'lment [" + e.Item.ItemIndex.ToString + "] du composant [Repeater]" End Sub Private Sub DataGrid1_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles DataGrid1.ItemCommand ' un vt s'est produit sur une des lignes du [datagrid] lblInfo.Text = "Vous avez cliqu sur le bouton [" + e.CommandName + "] de l'lment [" + e.Item.ItemIndex.ToString + "] de la page [" + DataGrid1.CurrentPageIndex.ToString() + "] du composant [DataGrid]" End Sub Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) Handles DataGrid1.PageIndexChanged ' chgt de page With DataGrid1 .CurrentPageIndex = e.NewPageIndex .DataSource = textes .DataBind() End With End Sub End Class

Commentaires : la source de donnes [textes] est un simple tableau de chanes de caractres. Elle sera lie aux trois composants prsents sur la page cette liaison est faite dans la procdure [Page_Load] lors de la premire requte. Pour les requtes suivantes, les trois composants retrouveront leurs valeurs par le mcanisme du [VIEW_STATE]. les trois composants ont un gestionnaire pour l'vnement [ItemCommand]. Celui-ci se produit lorsqu'un bouton ou un lien est cliqu dans l'une des lignes du composant. Le gestionnaire reoit deux informations : Composants serveur - 2 130/192

o o

source : la rfrence de l'objet (bouton ou lien) l'origine de l'vnement a : information sur l'vnement de type [DataListCommandEventArgs], [RepeaterCommandEventArgs], [DataGridCommandEventArgs] selon les cas. L'argument a apporte diverses informations avec lui. Ici, deux d'entre-elles nous intressent : a.Item : reprsente la ligne sur laquelle s'est produit l'vnement, de type [DataListItem], [DataGridItem] ou [RepeaterItem]. Quelque soit le type exact, l'lment [Item] a un attribut [ItemIndex] indiquant le numro de la ligne [Item] du le conteneur laquelle il appartient. Nous affichons ici ce numro de ligne a.CommandName : est l'attribut [CommandName] du bouton (Button, LinkButton, ImageButton) l'origine de l'vnement. Cette information conjugue la prcdente nous permet de savoir quel bouton du conteneur est l'origine de l'vnement [ItemCommand]

3.3 Application - gestion d'une liste d'abonnements


Maintenant que nous savons comment intercepter les vnements qui se produisent l'intrieur d'un conteneur de donnes, nous prsentons un exemple qui montre comment on peut les grer.

3.3.1 Introduction
L'application prsente simule une application d'abonnements des listes de diffusion. Celles-ci sont dfinies par un objet [DataTable] trois colonnes : nom
id thme description

type string string string

rle cl primaire nom du thme de la liste une description des sujets traits par la liste

Pour ne pas alourdir notre exemple, l'objet [DataTable] ci-dessus sera construit par code de faon arbitraire. Dans une application relle, il serait probablement fourni par une mthode d'une classe d'accs aux donnes. La construction de la table des listes se fera dans la procdure [Application_Start] et la table rsultante sera place dans l'application. Nous l'appellerons la table [dtThmes]. L'utilisateur va s'abonner certains des thmes de cette table. La liste de ses abonnements sera conserve l encore dans un objet [DataTable] appel [dtAbonnements] et dont la structure sera la suivante : nom
id thme

type string string

rle cl primaire nom du thme de la liste

L'application page unique est la suivante :

Composants serveur - 2

131/192

n
1 2 3 4 5 6

nom
dgThmes dlAbonnements panelInfos lblThme lblDescription lblInfo

type DataGrid DataList panel Label Label Label

proprits

fait partie de [panelInfos] fait partie de [panelInfos]

rle listes de diffusion proposes l'abonnement liste des abonnements de l'utilisateur aux listes prcdentes panneau d'informations sur le thme slectionn par l'utilisateur avec un lien [Plus d'informations] nom du thme description du thme message d'information de l'application

Notre exemple vise illustrer l'utilisation des composants [DataGrid] et [DataList] notamment la gestion des vnements qui se produisent au niveau des lignes de ces conteneurs de donnes. Aussi l'application n'a-t-elle pas de bouton de validation qui mmoriserait dans une base de donnes, par exemple, les choix faits par l'utilisateur. Nanmoins, il est raliste. Nous sommes proches d'une application de commerce lectronique o un utilisateur mettrait des produits (abonnements) dans son panier.

3.3.2 Fonctionnement
La premire vue qu'a l'utilisateur est la suivante :

VUE 1

L'utilisateur clique sur les liens [Plus d'informations] pour avoir des renseignements sur un thme. Ceux-ci sont affichs dans [panelInfos] :

Il clique sur les liens [S'abonner] pour s'abonner un thme. Les thmes choisis viennent s'inscrire dans le composant [dlAbonnements] :

Composants serveur - 2

132/192

L'utilisateur peut vouloir s'abonner une liste laquelle il est dj abonn. Un message le lui signale :

Enfin, il peut retirer son abonnement l'un des thmes avec les boutons [Retirer] ci-dessus. Aucune confirmation n'est demande. Ce n'est pas utile ici car l'utilisateur peut se rabonner aisment. Voici la vue aprs suppression de l'abonnement [thme1] :

Composants serveur - 2

133/192

3.3.3 Configuration des conteneurs de donnes


Le composant [dgThmes] de type [DataGrid] est li une source de type [DataTable]. Sa mise en forme a t faite avec le lien [Mise en forme automatique] du panneau des proprits du [DataGrid]. La dfinition de ses proprits a elle t faite avec le lien [Gnrateur de proprits] du mme panneau. Le code gnr est le suivant (le code de mise en forme est omis) :
<asp:datagrid id="dgThmes" AutoGenerateColumns="False" AllowPaging="True" PageSize="5" runat="server"> <ItemStyle ...></ItemStyle> <HeaderStyle ...></HeaderStyle> <FooterStyle ...></FooterStyle> <Columns> <asp:BoundColumn DataField="th&#232;me" HeaderText="Th&#232;me"></asp:BoundColumn> <asp:ButtonColumn Text="Plus d'informations" CommandName="infos"></asp:ButtonColumn> <asp:ButtonColumn Text="S'abonner" CommandName="abonner"></asp:ButtonColumn> </Columns> <PagerStyle HorizontalAlign="Center" ... Mode="NumericPages"></PagerStyle> </asp:datagrid>

On notera les points suivants :


AutoGenerateColumns=false AllowPaging=true PageSize=5 <asp:BoundColumn> <asp:ButtonColumn>

nous dfinissons nous-mmes les colonnes afficher dans la section <columns>...</columns> pour la pagination des donnes dfinit la colonne [thme] (HeaderText) du [DataGrid] qui sera lie la colonne [thme] de la source de donnes (DataField) dfinit deux colonnes de boutons (ou liens). Pour diffrentier les deux liens d'une mme ligne, on utilisera leur proprit [CommandName].

Le composant [DataGrid] n'est pas totalement paramtr. Il le sera dans le code du contrleur. Le composant [dlAbonnements] de type [DataList] est li une source de type [DataTable]. Sa mise en forme a t faite avec le lien [Mise en forme automatique] du panneau des proprits du [DataList]. La dfinition de ses proprits a elle t faite directement dans le code de prsentation. Ce code est le suivant (le code de mise en forme est omis) :
<asp:DataList id="dlAbonnements" ... runat="server" > <HeaderTemplate> <div align="center"> Vos abonnements</div> </HeaderTemplate> <ItemStyle ...></ItemStyle> <ItemTemplate> <TABLE> <TR> <TD><%#Container.DataItem("thme")%></TD> <TD> <asp:Button id="lnkRetirer" CommandName="retirer" runat="server" Text="Retirer" /></TD> </TR> </TABLE> </ItemTemplate> <HeaderStyle ...></HeaderStyle> </asp:DataList> <HeaderTemplate> <ItemTemplate>

dfinit le texte de l'entte du [DataList] dfinit l'lment courant du [DataList] - ici on a plac un tableau deux colonnes et une ligne. La premire cellule servira contenir le nom du thme auquel l'utilisateur veut s'abonner, l'autre le bouton [Retirer] qui lui permet d'annuler son choix.

3.3.4 La page de prsentation


Le code de prsentation [main.aspx] est le suivant :
<%@ page src="main.aspx.vb" inherits="main" autoeventwireup="false" %> <HTML> <HEAD> <title></title> </HEAD> <body> <P>Indiquez les thmes auxquels vous voulez vous abonner :</P> <HR width="100%" SIZE="1"> <form runat="server"> <table> <tr>

Composants serveur - 2

134/192

<td vAlign="top"> <asp:datagrid id="dgThmes" ... runat="server"> ... </asp:datagrid> </td> <td vAlign="top"> <asp:DataList id="dlAbonnements" ... runat="server" GridLines="Horizontal" ShowFooter="False"> .... </asp:DataList> </td> <td vAlign="top"> <asp:Panel ID="panelInfo" Runat="server" EnableViewState="False"> <TABLE> <TR> <TD bgColor="#99cccc"> <asp:Label id="lblThme" runat="server"></asp:Label></TD> </TR> <TR> <TD bgColor="#ffff99"> <asp:Label id="lblDescription" runat="server"></asp:Label></TD> </TR> </TABLE> </asp:Panel> </td> </tr> </table> <P> <asp:Label id="lblInfo" runat="server" EnableViewState="False"></asp:Label></P> </form> </body> </HTML>

3.3.5 Les contrleurs


Le contrle se rpartit entre les fichiers [global.asax] et [main.aspx]. Le fichier [global.asax] est le suivant :
<%@ Application src="global.asax.vb" inherits="global" %>

Le fichier associ [global.asax.vb] contient le code suivant :


Imports Imports Imports Imports System.Web System.Web.SessionState System.Data System

Public Class global Inherits System.Web.HttpApplication Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs) ' on initialise la source de donnes Dim thmes As New DataTable ' colonnes With thmes.Columns .Add("id", GetType(System.Int32)) .Add("thme", GetType(System.String)) .Add("description", GetType(System.String)) End With ' colonne id sera cl primaire thmes.Constraints.Add("clprimaire", thmes.Columns("id"), True) ' lignes Dim ligne As DataRow For i As Integer = 0 To 10 ligne = thmes.NewRow ligne.Item("id") = i.ToString ligne.Item("thme") = "thme" + i.ToString ligne.Item("description") = "description du thme " + i.ToString thmes.Rows.Add(ligne) Next ' on met la source de donnes dans l'application Application("thmes") = thmes End Sub Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs) ' dbut de session - on cre une table d'abonnements vide Dim dtAbonnements As New DataTable With dtAbonnements ' les colonnes .Columns.Add("id", GetType(String)) .Columns.Add("thme", GetType(String))

Composants serveur - 2

135/192

' la cl primaire .PrimaryKey = New DataColumn() {.Columns("id")} End With ' la table est mise dans la session Session.Item("abonnements") = dtAbonnements End Sub End Class

La procdure [Application_Start], excute lorsque l'application reoit sa toute premire requte, construit le [DataTable] des thmes auxquels on peut s'abonner. Il est construit arbitrairement avec du code. Rappelons la technique que nous avons dj rencontre. On construit dans l'ordre : un objet [DataTable] vide, sans structure et sans donnes la structure de la table en dfinissant ses colonnes (nom et type de donnes qu'elle contient) les lignes de la table qui reprsentent les donnes utiles Nous avons ici ajout une cl primaire. C'est la colonne "id" qui sert de cl primaire. Il y a plusieurs faons de le dire. Ici, nous avons utilis une contrainte. En SQL, une contrainte est une rgle que doivent observer les donnes d'une ligne pour que celle-ci puisse tre ajoute une table. Il existe toutes sortes de contraintes possibles. La contrainte "Primary Key" force la colonne laquelle elle est impose avoir des valeurs uniques et non vides. Une cl primaire peut tre en fait constitue d'une expression faisant intervenir des valeurs de plusieurs colonnes. [DataTable].Constraints est la collection des contraintes d'une table donne. Pour ajouter une contrainte, on utilise la mthode [DataTable.Constraints.Add]. Celle-ci a plusieurs signatures. Ici, on a utilis la mthode [Add(Byval nom as String, Byval colonne as DataColumn, Byval clPrimaire as Boolean)] :
nom colonne clPrimaire

nom de la contrainte - peut tre quelconque colonne qui sera cl primaire - de type [DataColumn] doit tre [vrai] pour faire de [colonne] une cl primaire. Si [clPrimaire=false], on a seulement la contrainte de valeurs uniques sur [colonne]

Pour faire de la colonne nomme "id" la cl primaire de la table [dtAbonnements], on crit donc :
dtAbonnements.Constraints.Add("xxx",dtAbonnements.Columns("id"),true)

La procdure [Session_Start], excute lorsque l'application reoit la premire requte d'un client. Elle sert crer des objets propres chaque client qui doivent persister au travers des diffrentes requtes de celui-ci. La procdure construit le [DataTable] des abonnements du client. Seule sa structure est construire puisqu'au dpart cette table est vide. Elle va se remplir au fil des requtes. L galement, la colonne "id" sert de cl primaire. On a utilis une technique diffrente pour dclarer cette contrainte :
[DataTable].PrimaryKey

est le tableau des colonnes formant la cl primaire - ici on a dclar un tableau un lment : la colonne de nom "id"

Lorsque la requte du client atteint le contrleur [main.aspx], les deux objets [DataTable] sont disponibles dans l'application pour la table des thmes et dans la session pour la table des abonnements. Le contrleur [main.aspx.vb] est le suivant :
Imports System.Data Public Class main Inherits System.Web.UI.Page Protected Protected Protected Protected Protected Protected WithEvents WithEvents WithEvents WithEvents WithEvents WithEvents dgThmes As System.Web.UI.WebControls.DataGrid lblThme As System.Web.UI.WebControls.Label lblDescription As System.Web.UI.WebControls.Label dlAbonnements As System.Web.UI.WebControls.DataList lblInfo As System.Web.UI.WebControls.Label panelInfo As System.Web.UI.WebControls.Panel

Protected dtThmes As DataTable Protected dtAbonnements As DataTable Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ... End Sub Private Sub liaisons() ... End Sub Private Sub dgThmes_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) Handles dgThmes.PageIndexChanged ... End Sub

Composants serveur - 2

136/192

Private Sub dgThmes_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles dgThmes.ItemCommand ... End Sub Private Sub infos(ByVal id As String) ... End Sub Private Sub abonner(ByVal id As String) ... End Sub Private Sub dlAbonnements_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataListCommandEventArgs) Handles dlAbonnements.ItemCommand ... End Sub End Class

La procdure [Page_Load] a pour rle essentiel de : rcuprer les deux tables [dtThmes] et [dtAbonnements] qui sont respectivement dans l'application et la session afin de les rendre disponibles toutes les mthodes de la page faire la liaison de donnes de ces deux sources avec leurs conteneurs respectifs. Ceci est fait seulement lors de la premire requte. Lors des requtes suivantes, la liaison n'est pas faire systmatiquement et lorsqu'elle est faire, il faut parfois attendre un vnement postrieur [Page_Load] pour la faire. Le code de [Page_Load] est le suivant :
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ' on rcupre les sources de donnes dtThmes = CType(Application("thmes"), DataTable) dtAbonnements = CType(Session("abonnements"), DataTable) ' liaison de donnes If Not IsPostBack Then liaisons() End If ' on cache certaines informations panelInfo.Visible = False End Sub Private Sub liaisons() 'on lie la source de donnes au composant [datagrid] With dgThmes .DataSource = dtThmes .DataKeyField = "id" End With ' on lie la source de donnes au composant [datalist] With dlAbonnements .DataSource = dtAbonnements .DataKeyField = "id" End With ' on assigne les donnes aux composants Page.DataBind() End Sub

Dans la procdure [liaisons], nous utilisons la proprit [DataKeyField] des composants [DataList] et [DataGrid] pour dfinir la colonne de la source de donnes qui servira identifier de faon unique les lignes des conteneurs. Classiquement, cette colonne est la cl primaire de la source de donnes mais ce n'est pas obligatoire. Il suffirait que la colonne soit exempte de doublons et de valeurs vides. Pour le conteneur [dgThmes], c'est la colonne "id" de la source [dtThmes] qui servira de cl primaire et pour le conteneur [dlAbonnements] ce sera la colonne "id" de la source [dtAbonnements]. Il n'y a pas ncessit ce que la colonne qui sert de cl primaire au conteneur soit affiche par celui-ci. Ici, aucun des deux conteneurs n'affiche la colonne cl primaire. L'intrt qu'un conteneur ait une cl primaire est que celle-ci permet de retrouver aisment dans la source de donnes, les informations lies la ligne du conteneur sur laquelle s'est produit un vnement. En effet, il est frquent qu' partir de la ligne d'un conteneur sur laquelle s'est produit un vnement, on doive agir sur la ligne correspondante de la source de donnes qui lui est lie. La cl primaire facilite ce travail. La pagination du [DataGrid] est gre de faon classique :
Private Sub dgThmes_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) Handles dgThmes.PageIndexChanged ' chgt de page dgThmes.CurrentPageIndex = e.NewPageIndex ' liaison liaisons()

Composants serveur - 2

137/192

End Sub

Les actions sur les liens [Plus d'informations] et [S'abonner] sont gres par la procdure [dgThmes_ItemCommand] :
Private Sub dgThmes_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles dgThmes.ItemCommand ' vt sur une ligne du [datagrid] Dim commande As String = e.CommandName Select Case commande Case "infos" infos(dgThmes.DataKeys(e.Item.ItemIndex)) Case "abonner" abonner(dgThmes.DataKeys(e.Item.ItemIndex)) End Select ' liaison liaisons() End Sub

On utilise le fait que les deux liens ont un attribut [CommandName] pour les diffrentier. Selon la valeur de cet attribut, on appelle la procdure [infos] ou [abonner] en passant dans les deux cas la cl "id" associe l'lment du [DataGrid] o s'est produit l'vnement. Munie de cette information, la procdure [info] va afficher les informations du thme choisi par l'utilisateur :
Private Sub infos(ByVal id As String) ' informations sur le thme de cl id ' on rcupre la ligne du [datatable] correspondant la cl Dim ligne As DataRow ligne = dtThmes.Rows.Find(id) If Not ligne Is Nothing Then ' on affiche l'info lblThme.Text = CType(ligne("thme"), String) lblDescription.Text = CType(ligne("description"), String) panelInfo.Visible = True End If End Sub

La table [dtThmes] ayant une cl primaire, la mthode [dtThmes.Rows.Find("P")] permet de trouver la ligne ayant la cl primaire P. Si elle est trouve, on obtient un objet [DataRow]. Ici, nous devons chercher la ligne ayant la cl primaire [id] o [id] est pass en paramtre. Si la ligne est trouve, on met les informations [thme] et [description] de cette ligne dans le panneau d'informations qu'on rend ensuite visible. La procdure [abonner(id)] doit ajouter le thme de cl [id] la liste des abonnements. Son code est le suivant :
Private Sub abonner(ByVal id As String) ' abonnement au thme de cl id ' on rcupre la ligne du [datatable] correspondant la cl Dim ligne As DataRow ligne = dtThmes.Rows.Find(id) If Not ligne Is Nothing Then ' on vrifie si on n'est pas dj abonn Dim abonnement As DataRow abonnement = dtAbonnements.Rows.Find(id) If Not abonnement Is Nothing Then ' on signale l'erreur lblInfo.Text = "Vous tes dj abonn au thme [" + ligne("thme") + "]" Else ' on ajoute le thme aux abonnements abonnement = dtAbonnements.NewRow abonnement("id") = id abonnement("thme") = ligne("thme") dtAbonnements.Rows.Add(abonnement) ' on fait les liaisons liaisons() End If End If End Sub

Dans la liste des thmes [dtThmes], on cherche tout d'abord la ligne de cl [id]. Si on la trouve, on vrifie que ce thme n'est pas dj prsent dans la liste des abonnements pour viter de l'inscrire deux fois. Si c'est le cas, on affiche un mesage d'erreur. Sinon, on ajoute un nouvel abonnement la table [dtAbonnements] et on fait les liaisons des composants de liste de donnes leurs sources respectives. Lorsque l'utilisateur clique sur un bouton [Retirer], on doit supprimer un lment de la table [dtAbonnements]. Cela est fait par la procdure suivante :

Composants serveur - 2

138/192

Private Sub dlAbonnements_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataListCommandEventArgs) Handles dlAbonnements.ItemCommand ' on retire un abonnement Dim commande As String = e.CommandName If commande = "retirer" Then ' on retire l'abonnement du [datatable] With dtAbonnements.Rows .Remove(.Find(dlAbonnements.DataKeys(e.Item.ItemIndex))) End With ' liaisons liaisons() End If End Sub

On vrifie tout d'abord l'attribut [CommandName] de l'lment l'origne de l'vnement. C'est en fait assez inutile puisque le bouton [Retirer] est le seul contrle capable de gnrer un vnement dans le composant [DataList]. Il n'y a donc pas d'ambigut. Pour supprimer une ligne d'un objet [DataTable], on utilise la mthode [DataList.Remove(DataRow)] qui enlve de la table, la ligne de type [DataRow] passe en paramtre. Celle-ci est trouve par la mthode [DataList.Find] qui on a pass la cl primaire de la ligne cherche. Une fois la ligne dtruite, on fait la liaison des donnes aux composants

3.4 Grer un [DataList] pagin


Nous reprenons l'exemple prcdent afin de paginer le composant [DataList] reprsentant la liste des abonnements de l'utilisateur. Contrairement au composant [DataGrid], le composant [DataList] n'offre aucune facilit pour la pagination. Nous allons voir que la mise en place de celle-ci est complexe, ce qui nous fera apprcier sa juste valeur la pagination automatique du [DataGrid].

3.4.1 Fonctionnement
La seule diffrence est la pagination du [DataList]. Les pages auront deux abonnements. Si l'utilisateur a cinq abonnements, on aura trois pages. La premire sera la suivante :

La deuxime page est obtenue via le lien [Suivant] :

La troisime page : Composants serveur - 2

139/192

On remarquera que les liens [Prcdent] et [Suivant] ne sont visibles que s'il y a respectivement une page qui prcde et une page qui suit la page courante.

3.4.2 Code de prsentation


Les liens [Prcdent] et [Suivant] sont obtenus en ajoutant une balise <FooterTemplate> au [DataList] :
<asp:datalist id="dlAbonnements" runat="server" ...> .... <FooterTemplate> <asp:LinkButton id="lnkPrecedent" runat="server" CommandName="precedent">Prcdent</asp:LinkButton> <asp:LinkButton id="lnkSuivant" runat="server" CommandName="suivant">Suivant</asp:LinkButton> </FooterTemplate> .... </asp:datalist>

3.4.3 Code de contrle


Le fichier associ [global.asax.vb] volue de la faon suivante :
... Public Class global Inherits System.Web.HttpApplication Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs) ... End Sub Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs) ' dbut de session - on cre une table d'abonnements vide Dim dtAbonnements As New DataTable With dtAbonnements ' les colonnes .Columns.Add("id", GetType(String)) .Columns.Add("thme", GetType(String)) ' la cl primaire .PrimaryKey = New DataColumn() {.Columns("id")} End With ' la table est mise dans la session Session.Item("abonnements") = dtAbonnements ' la page courante est la page 0 Session.Item("pAC") = 0 ' le nombre d'abonnements dans cette page est 0 Session.Item("nbAC") = 0 End Sub

Outre la table des abonnements [dtAbonnements], on met dans la session deux autres informations :
pAC

de type [Integer] - c'est le n de la page courante affiche lors de la dernire requte 140/192

Composants serveur - 2

nbAC

de type [Integer] - nbre de lignes affiches dans la page courante prcdente

Au dbut d'une session, le n de page courante ainsi que le nombre de lignes de cette page sont nuls. Le contrleur [main.aspx.vb] volue de la faon suivante :
.... Public Class main Inherits System.Web.UI.Page .... ' donnes Protected Protected Protected Protected Protected Protected de l'application dtThmes As DataTable dtAbonnements As DataTable dtPA As DataTable ' la page d'abonnements affiche Const nbAP As Integer = 2 ' nbre abonnements par page pAC As Integer ' page abonnement courant nbAC As Integer ' nombre d'abonnements dans page courante

Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ... End Sub Private Sub terminer() ... End Sub Private Sub dgThmes_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) Handles dgThmes.PageIndexChanged ... End Sub Private Sub dgThmes_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles dgThmes.ItemCommand ... End Sub Private Sub infos(ByVal id As String) ... End Sub Private Sub abonner(ByVal id As String) ... End Sub Private Sub dlAbonnements_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataListCommandEventArgs) Handles dlAbonnements.ItemCommand ... End Sub Private Sub changePAC() ... End Sub Private Sub setLiens(ByVal ctl As Control, ByVal blPrec As Boolean, ByVal blSuivant As Boolean) ... End Sub End Class

On y dfinit de nouvelles donnes lies la pagination des abonnements :


Protected Protected Protected Protected dtPA As DataTable ' la page d'abonnements affiche Const nbAP As Integer = 2 ' nbre abonnements par page pAC As Integer ' page abonnement courant nbAC As Integer ' nombre d'abonnements dans page courante

Certaines de ces informations sont stockes dans la session et sont rcupres chaque requte dans la procdure [Page_Load] :
Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ' on rcupre les sources de donnes dtThmes = CType(Application("thmes"), DataTable) dtAbonnements = CType(Session("abonnements"), DataTable) ' et les informations d'affichage des abonnements pAC = CType(Session("pAC"), Integer) nbAC = CType(Session("nbAC"), Integer)

Composants serveur - 2

141/192

' liaison de donnes If Not IsPostBack Then ' affichage d'une liste d'abonnements vide terminer() End If ' on cache certaines informations panelInfo.Visible = False End Sub

Les informations rcupres [pAC] et [nbAC] sont des informations sur la page d'abonnements affiche lors de la prcdente requte :
pAC nbAC

c'est le n de la page courante affiche lors de la prcdente requte nombre de lignes affiches dans cette page courante

La mthode [terminer] fait les liaisons des composants avec leurs sources de donnes comme le faisait la mthode [liaisons] dans l'application prcdente. La nouveaut, ici, est la liaison du [DataList] avec la table [dtPA] qui est la page d'abonnements afficher :
Private Sub terminer() ' on lie la source de donnes au composant [datagrid] With dgThmes .DataSource = dtThmes .DataKeyField = "id" End With ' on lie la page d'abonnements au composant [datalist] en tenant compte de la page courante pAC changePAC() ' on visualise la page p With dlAbonnements .DataSource = dtPA .DataKeyField = "id" End With ' on assigne les donnes aux composants Page.DataBind() ' gestion des liens [prcdent] et [suivant] du [datalist] Dim blprec As Boolean = pAC <> 0 Dim blsuivant As Boolean = pAC <> (dtAbonnements.Rows.Count - 1) \ nbAP Dim nbLiensTrouvs As Integer = 0 setLiens(dlAbonnements, blprec, blsuivant, nbLiensTrouvs) ' on sauvegarde les informations de page courante dans la session Session("pAC") = pAC Session("nbAC") = dtPA.Rows.Count End Sub

Les points suivants sont noter : la source [dtPA] dpend du n de page courante [pAC] afficher. La variable [pAC] est une variable globale de la classe, manipule par les mthodes qui ont faire voluer ce n de page courante. C'est la mthode [changePAC] qui fait le travail de construction de la table [dtPA] qui sera lie au composant [dlAbonnements]. la mthode [setLiens] a pour rle d'afficher ou de cacher les liens [Prcdent] et [Suivant] selon que la page courante [pAC] affiche est prcde et suivie d'une page. Elle a quatre paramtres : o [dlAbonnements] : le contrle [DataList] dont on va explorer l'arborescence des contrles pour trouver les deux liens. En effet, bien que ces deux liens soient dans un endroit prcis qui est le pied-de-page du [DataList], il ne semble pas qu'il y ait un moyen simple de les rfrencer directement. En tout cas, il n'a pas t trouv ici. o [blPrecedent] : boolen affecter la proprit [visible] du lien [Precedent] - est vrai si la page courante est diffrente de 0 o [blSuivant] : boolen affecter la proprit [visible] du lien [Suivant] - est vrai si la page courante n'est pas la dernire page de la liste des abonnements o [nbLiensTrouvs] : un paramtre de sortie qui compte le nombre de liens trouvs. Ds que cette quantit est gale deux, la mthode est termine. les informations [pAC] et [nbAC] sont sauvegardes dans la session pour la prochaine requte. La mthode [changePAC] construit la table [dtPA] qui sera lie au composant [dlAbonnements]. Elle le fait d'aprs le n [pAC] de la page courante afficher. La table [dtPA] doit afficher certaines lignes de la table des abonnements [dtAbonnements]. Rappelons que cette table est mmorise dans la session et mise jour (augmente ou diminue) au fil des requtes. On commence par fixer l'intervalle [premier,dernier] des ns de lignes de la table [dtAbonnements] que la table [dtPA] doit afficher :
Private Sub changePAC() ' fait de la page pAC, la page courante des abonnements ' gestion des pages du [datalist] Dim nbAbonnements = dtAbonnements.Rows.Count Dim dernirePage = (nbAbonnements - 1) \ nbAP ' premier et dernier abonnement If pAC < 0 Then pAC = 0 If pAC > dernirePage Then pAC = dernirePage

Composants serveur - 2

142/192

Dim premier As Integer = pAC * nbAP Dim dernier As Integer = (pAC + 1) * nbAP - 1 If dernier > nbAbonnements - 1 Then dernier = nbAbonnements - 1

Ceci fait, on peut construire la table [dtPA]. On dfinit tout d'abord sa structure [id,thme] puis on la remplit en y recopiant les lignes de [dtAbonnements] dont le n est dans l'intervalle [premier,dernier] calcul prcdemment.
' cration du datatable dtpa dtPA = New DataTable With dtPA ' les colonnes .Columns.Add("id", GetType(String)) .Columns.Add("thme", GetType(String)) ' la cl primaire .PrimaryKey = New DataColumn() {.Columns("id")} End With Dim abonnement As DataRow For i As Integer = premier To dernier abonnement = dtPA.NewRow With abonnement .Item("id") = dtAbonnements.Rows(i).Item("id") .Item("thme") = dtAbonnements.Rows(i).Item("thme") End With dtPA.Rows.Add(abonnement) Next End Sub

A la fin de la mthode [changePAC], la table [dtPA] a t construite et peut tre lie au composant [DataList]. C'est fait dans la mthode [terminer]. Dans cette mme mthode, on utilise la procdure [setLiens] pour fixer l'tat des liens [Prcdent] et [Suivant] du [DataList]. Le code de cette procdure est le suivant :
Private Sub setLiens(ByVal ctl As Control, ByVal blPrec As Boolean, ByVal blSuivant As Boolean, ByRef nbLiensTrouvs As Integer) ' on recherche les liens [prcdent] et [suivant] ' dans l'arborescence des contrles du [datalist] ' a-t-on trouv tous les liens ? If nbLiensTrouvs = 2 Then Exit Sub ' examen des contrles enfant Dim c As Control For Each c In ctl.Controls ' on travaille d'abord en profondeur - les liens sont au fond de l'arbre setLiens(c, blPrec, blSuivant, nbLiensTrouvs) ' lien [Prcdent] ? If c.ID = "lnkPrecedent" Then CType(c, LinkButton).Visible = blPrec nbLiensTrouvs += 1 End If ' lien [Suivant] ? If c.ID = "lnkSuivant" Then CType(c, LinkButton).Visible = blSuivant nbLiensTrouvs += 1 End If Next End Sub

La procdure est rcursive. Elle cherche tout d'abord, parmi les contrles enfants du composant [dlAbonnements] des composants s'appelant [lnkPrecedent] et [lnkSuivant] qui sont les identits (attribut ID) des deux liens de pagination. Elle les cherche d'abord en partant du fond de l'arbre des contrles car c'est l qu'ils sont. Ds qu'un lien a t trouv, le compteur [nbLiensTrouvs] est incrment et la proprit [visible] du lien est renseigne avec une valeur passe en paramtre de la procdure. Ds que les deux liens ont t trouvs, l'arbre des contrles n'est plus explor et la procdure rcursive se termine. Nous avons dit que la mthode [changePAC] qui fixe la source de donnes [dtPA] pour le composant [dlAbonnements] travaillait avec le numro [pAC] de la page courante afficher. Plusieurs procdures modifient ce numro :
Private Sub abonner(ByVal id As String) ' abonnement au thme de cl id .. ' on ajoute le thme aux abonnements .. ' mise jour du n de page courante - c'est maintenant la dernire page pAC = (dtAbonnements.Rows.Count - 1) \ nbAP ' liaisons de donnes terminer() End If End Sub

Composants serveur - 2

143/192

Aprs l'ajout d'un abonnement, celui-ci se retrouve en fin de liste des abonnements. Aussi se positionne-t-on sur la dernire page des abonnements afin que l'utilisateur voit l'ajout qui a t opr.
Private Sub dlAbonnements_ItemCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataListCommandEventArgs) Handles dlAbonnements.ItemCommand ' on retire un abonnement Dim commande As String = e.CommandName Select Case commande Case "retirer" ' on retire l'abonnement du [datatable] With dtAbonnements.Rows .Remove(.Find(dlAbonnements.DataKeys(e.Item.ItemIndex))) End With ' faut-il changer de page courante ? nbAC -= 1 If nbAC = 0 Then pAC -= 1 Case "precedent" ' chgt de page courante pAC -= 1 Case "suivant" ' chgt de page courante pAC += 1 End Select ' liaisons de donnes terminer() End Sub

[nbAC] est le nombre de lignes affiches dans la page courante avant le retrait d'un abonnement. Si le nouveau nombre de lignes de la page est gal 0, le n de page courante [pAC] est dcrment d'une unit. en cas de clic sur le lien [Precedent], le le n de page courante [pAC] est dcrment d'une unit. en cas de clic sur le lien [Suivant], le n de page courante [pAC] est incrment d'une unit.

Les autres procdures restent identiques ce qu'elles taient prcdemment.

3.4.4 Conclusion
Nous avons montr sur cet exemple que nous pouvions paginer un composant [DataList]. Cette pagination est dlicate et il est prfrable de s'en remettre la pagination automatique du composant [DataGrid] quand cela est possible. Cet exemple nous a galement montr comment atteindre les composants prsents dans le pied-de-page du composant [DataList].

3.5 Classe d'accs une base de produits


Nous nous intressons de nouveau la base de donnes ACCESS [produits] dj utilise. Rappelons qu'elle a une unique table appele [liste] dont la structure est la suivante :

Nous allons construire une classe d'accs la table [liste] qui permettra de la lire et de la mettre jour. Nous construirons galement un client console qui se servira de la classe prcdente pour mettre jour la table. Dans un deuxime temps, nous construirons un client web pour faire ce mme travail.

3.5.1 La classe ExceptionProduits


La classe [Exception] a un constructeur qui admet un message d'erreur comme paramtre. Nous souhaitons ici disposer d'une classe d'exception disposant d'un constructeur admettant une liste de messages d'erreurs plutt qu'un message d'erreur unique. Ce sera la classe [ExceptionProduits] ci-dessous :
Public Class ExceptionProduits Inherits Exception ' msg d'erreurs lies l'exception Private _erreurs As ArrayList ' constructeur Public Sub New(ByVal erreurs As ArrayList) Me._erreurs = erreurs End Sub

Composants serveur - 2

144/192

' proprit Public ReadOnly Property erreurs() As ArrayList Get Return _erreurs End Get End Property End Class

3.5.2 La structure [sProduit]


La structure [produit] reprsentera un produit [id, nom, prix] :
' structure sProduit Public Structure sProduit ' les champs Private _id As Integer Private _nom As String Private _prix As Double ' proprit id Public Property id() As Integer Get Return _id End Get Set(ByVal Value As Integer) _id = Value End Set End Property ' proprit nom Public Property nom() As String Get Return _nom End Get Set(ByVal Value As String) If IsNothing(Value) OrElse Value.Trim = String.Empty Then Throw New Exception _nom = Value End Set End Property ' proprit prix Public Property prix() As Double Get Return _prix End Get Set(ByVal Value As Double) If IsNothing(Value) OrElse Value < 0 Then Throw New Exception _prix = Value End Set End Property End Structure

La structure n'admet que des donnes valides pour les champs [nom] et [prix].

3.5.3 La classe Produits


La classe [Produits] est la classe qui va nous permettre de mettre jour la table [liste] de la base de produits. Sa structure est la suivante :
Public Class produits ' donnes d'instance Public Sub New(ByVal chaineConnexionOLEDB As String) .... End Sub Public Function getProduits() As DataTable .... End Function Public Sub ajouterProduit(ByVal produit As sProduit) ... End Sub Public Sub modifierProduit(ByVal produit As sProduit) ... End Sub

Composants serveur - 2

145/192

Public Sub supprimerProduit(ByVal id As Integer) ... End Sub End Class

Les donnes d'instance


Les donnes partages par les diffrentes mthodes de la classe sont les suivantes :
Private connexion As OleDbConnection Private Const selectText As String = "select Private Const insertText As String = "insert Private Const updateText As String = "update Private Const deleteText As String = "delete Private selectCommand As New OleDbCommand Dim insertCommand As New OleDbCommand Dim updateCommand As New OleDbCommand Dim deleteCommand As New OleDbCommand Dim adaptateur As New OleDbDataAdapter connexion selectText insertText updateText deleteText selectCommand updateCommand insertCommand deleteCommand adaptateur id,nom,prix from liste" into liste(nom,prix) values(?,?)" liste set nom=?,prix=? where id=?" from liste where id=?"

la connexion la base de donnes - sera ouverte pour l'excution d'une commande SQL puis referme aussitt aprs requte SQL [select] obtenant toute la table [liste] requte permettant l'insertion d'une ligne (nom, prix) dans la table [liste]. On remarquera qu'on ne prcise pas le champ [id]. En effet, ce champ est auto-incrment par le SGBD et donc nous n'avons pas le spcifier. requte permettant la mise jour des champs (nom,prix) de la ligne de la table [liste] ayant la cl [id] requte permettant la suppression de la ligne de la table [liste] ayant la cl [id] objet [OleDbCommand] excutant la requte [selectText] sur la connexion [connexion] objet [OleDbCommand] excutant la requte [updateText] sur la connexion [connexion] objet [OleDbCommand] excutant la requte [insertText] sur la connexion [connexion] objet [OleDbCommand] excutant la requte [deleteText] sur la connexion [connexion] objet permettant de rcuprer le rsultat de l'excution de [selectCommand] dans un objet [DataSet]

Le constructeur
Le constructeur reoit un unique paramtre [chaineConnexionOLEDB] qui est la chane de connexion dsignant la bse de donnes exploiter. A partir de celle-ci, on prpare les quatre commandes d'exploitation et de mise jour de la table ainsi que l'adaptateur. Ce n'est qu'une prparation et aucune connexion n'est faite.
Public Sub New(ByVal chaineConnexionOLEDB As String) ' on prpare la connexion connexion = New OleDbConnection(chaineConnexionOLEDB) ' on prpare les commandes de requtes Dim commandes() As OleDbCommand = {selectCommand, insertCommand, updateCommand, deleteCommand} Dim textes() As String = {selectText, insertText, updateText, deleteText} For i As Integer = 0 To commandes.Length - 1 With commandes(i) .CommandText = textes(i) .Connection = connexion End With Next ' on prpare l'adaptateur d'accs aux donnes adaptateur.SelectCommand = selectCommand End Sub

La mthode getProduits
Cette mthode permet d'avoir le contenu de la table [Liste] dans un objet [DataTable]. Son code est le suivant :
Public Function getProduits() As DataTable ' on met la table [liste] dans un [dataset] Dim contenu As New DataSet ' on cre un objet DataAdapter pour lire les donnes de la source OLEDB Try With adaptateur .FillSchema(contenu, SchemaType.Source) .Fill(contenu) End With Catch e As Exception ' pb Dim erreursCommande As New ArrayList erreursCommande.Add(String.Format("Erreur d'accs la base de donnes : {0}", e.Message))

Composants serveur - 2

146/192

Throw New ExceptionProduits(erreursCommande) End Try ' on rend le rsultat Return contenu.Tables(0) End Function

Le travail est fait par les deux instructions suivantes :


With adaptateur .FillSchema(contenu, SchemaType.Source) .Fill(contenu) End With

La mthode [FillSchema] fixe la structure (colonnes, contraintes, relations) du [DataSet] contenu partir de la structure de la base rfrence par [adaptateur.Connexion]. Cela nous permet de rcuprer la structure de la table [liste] et notamment sa cl primaire. L'opration [Fill] qui suit remplit le [Dataset] contenu avec les lignes de la table [liste]. Avec cette seule opration, on aurait bien eu les donnes et la structure mais pas la cl primaire. Or celle-ci nous sera utile pour mettre jour la table [liste] en mmoire. Ici, comme dans les autres mthodes, nous grons les ventuelles erreurs l'aide de la classe [ExceptionProduits] afin d'obtenir une liste (ArrayList) d'erreurs plutt qu'une seule erreur. La mthode [getProduits] rend la table [liste] sous la forme d'un objet [DataTable].

La mthode ajouterProduits
Cette mthode permet d'ajouter une ligne (id, nom, prix) la table [liste]. Ces informations lui sont donnes sous la forme d'une structure [sProduit] de champs [id, nom, prix]. Le code de la mthode est le suivant :
Public Sub ajouterProduit(ByVal produit As sProduit) ' on ajoute un produit [nom,prix] ' on prpare les paramtres de l'ajout With insertCommand.Parameters .Clear() .Add(New OleDbParameter("nom", produit.nom)) .Add(New OleDbParameter("prix", produit.prix)) End With ' on fait l'ajout Try ' ouverture connexion connexion.Open() ' excution commande insertCommand.ExecuteNonQuery() Catch ex As Exception ' pb Dim erreursCommande As New ArrayList erreursCommande.Add(String.Format("Erreur lors de l'ajout : {0}", ex.Message)) Throw New ExceptionProduits(erreursCommande) Finally ' fermeture connexion connexion.Close() End Try End Sub

Les champs de la structure [produit] sont injects dans les paramtres de la commande [insertCommand]. Rappelons la configuration actuelle de cette commande (cf constructeur) :
Private Const insertText As String = "insert into liste(nom,prix) values(?,?)" insertCommand.Connexion=connexion

Le texte de la commande SQL [insert] comporte des paramtres formels ? qu'il faut remplacer par des paramtres effectifs. Cela se fait avec la collection [Parameters] de la classe [OleDbCommand]. Celle-ci contient des lments de type [OleDbParameter] qui dfinissent les paramtres effectifs qui doivent remplacer les paramtres formels ?. Comme ces derniers ne sont pas nomms, c'est l'index des paramtres effectifs qui est utilis pour savoir quel paramtre formel correspond tel paramtre effectif. Ici, le paramtre effectif n i dans la collection [Parameters] remplacera le paramtre formel ? n i. Pour crer un paramtre effectif de type [OleDbParameter] on utilise ici le constructeur [OleDbParameter (Byval nom as String, Byval valeur as Object)] qui dfinit le nom et la valeur du paramtre effectif. Le nom peut tre quelconque. De plus ici, il ne sera pas utilis. Les deux paramtres de l'instruction SQL [insert] reoivent pour valeurs celles des champs [nom, prix] de la structure [produit]. Ceci fait, l'insertion est faite par l'instruction [insertCommand.ExecuteNonQuery].

La mthode modifierProduits
Cette mthode permet de modifier la ligne de la table [liste]. Les informations qui lui sont ncessaires lui sont donnes sous la structure [sProduit] de champs [id, nom, prix]. Public Sub modifierProduit(ByVal Composants serveur - 2
produit As sProduit)

147/192

' on modifie un produit [id,nom,prix] ' on prpare les paramtres de la mise jour With updateCommand.Parameters .Clear() .Add(New OleDbParameter("nom", produit.nom)) .Add(New OleDbParameter("prix", produit.prix)) .Add(New OleDbParameter("id", produit.id)) End With ' on fait la modification Try ' ouverture connexion connexion.Open() ' excution commande Dim nbLignes As Integer = updateCommand.ExecuteNonQuery() If nbLignes = 0 Then Throw New Exception(String.Format("Le produit de cl [{0}] n'existe pas dans la table des donnes", produit.id)) Catch ex As Exception ' pb Dim erreursCommande As New ArrayList erreursCommande.Add(String.Format("Erreur lors de la modification : {0}", ex.Message)) Throw New ExceptionProduits(erreursCommande) Finally ' fermeture connexion connexion.Close() End Try End Sub

Le code est quasi identique celui de la mthode [ajouterProduits] si ce n'est que la commande [OleDbCommand] concerne est [updateCommand] au lieu de [insertCommand].

La mthode supprimerProduits
Cette mthode permet de suprimer la ligne de la table [liste] de cl [id] passe en paramtre. Le code est le suivant :
Public Sub supprimerProduit(ByVal id As Integer) ' supprime le produit de cl [id] ' on prpare les paramtres de la suppression With deleteCommand.Parameters .Clear() .Add(New OleDbParameter("id", id)) End With ' on fait la suppression Try ' ouverture connexion connexion.Open() ' excution commande Dim nbLignes As Integer = deleteCommand.ExecuteNonQuery() If nbLignes = 0 Then Throw New Exception(String.Format("Le produit de cl [{0}] n'existe pas dans la table des donnes", id)) Catch ex As Exception ' pb Dim erreursCommande As New ArrayList erreursCommande.Add(String.Format("Erreur lors de la suppression : {0}", ex.Message)) Throw New ExceptionProduits(erreursCommande) Finally ' fermeture connexion connexion.Close() End Try End Sub

On retrouve la mme dmarche que pour les mthodes prcdentes.

3.5.4 Tests de la classe [produits]


Un programme de tests [testproduits.vb] de type console, pourrait tre le suivant :
Option Explicit On Option Strict On ' espaces de noms Imports System Imports System.Data Imports Microsoft.VisualBasic Imports System.Collections Namespace st.istia.univangers.fr ' pg de test

Composants serveur - 2

148/192

Module testproduits Dim contenu As DataTable Sub Main(ByVal arguments() As String) ' affiche le contenu d'une table de produits ' la table est dans une base ACCESS dont le pg reoit le nom de fichier Const syntaxe1 As String = "pg bdACCESS" ' vrification des paramtres du programme If arguments.Length <> 1 Then ' msg d'erreur Console.Error.WriteLine(syntaxe1) ' fin Environment.Exit(1) End If ' on prpare la chane de connexion Dim chaineConnexion As String = "Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=" + arguments(0) ' cration d'un objet produits Dim objProduits As produits = New produits(chaineConnexion) ' on affiche tous les produits afficheProduits(objProduits) ' on insre un produit Dim produit As New sProduit With produit .nom = "xxx" .prix = 1 End With Try objProduits.ajouterProduit(produit) Catch ex As ExceptionProduits afficheErreurs(ex.erreurs) End Try ' on affiche tous les produits afficheProduits(objProduits) ' on rcupre l'id du produit ahjout produit.id = CType(contenu.Rows(contenu.Rows.Count - 1)("id"), Integer) ' on modifie le produit ajout produit.prix = 200 Try objProduits.modifierProduit(produit) Catch ex As ExceptionProduits afficheErreurs(ex.erreurs) End Try ' on affiche tous les produits afficheProduits(objProduits) ' on supprime le produit ajout Try objProduits.supprimerProduit(produit.id) Catch ex As ExceptionProduits afficheErreurs(ex.erreurs) End Try ' on affiche tous les produits afficheProduits(objProduits) End Sub Sub afficheProduits(ByRef objProduits As produits) ' on rcupre la table des produits dans un datatable Try contenu = objProduits.getProduits() Catch ex As ExceptionProduits afficheErreurs(ex.erreurs) Environment.Exit(2) End Try Dim lignes As DataRowCollection = contenu.Rows For i As Integer = 0 To lignes.Count - 1 ' ligne i de la table Console.Out.WriteLine(lignes(i).Item("id").ToString + "," + lignes(i).Item("nom").ToString + _ "," + lignes(i).Item("prix").ToString) Next ' stoppe le flux console Console.WriteLine("...") Console.ReadLine() End Sub Sub afficheErreurs(ByRef erreurs As ArrayList) ' affiche les erreurs sur la console If erreurs.Count <> 0 Then Console.WriteLine("Les erreurs suivantes se sont produites :") For i As Integer = 0 To erreurs.Count - 1 Console.WriteLine(String.Format("-- {0}", CType(erreurs(i), String)))

Composants serveur - 2

149/192

Next End If ' stoppe le flux console Console.WriteLine("...") Console.ReadLine() End Sub End Module End Namespace

On compile les deux fichiers source :


dos>vbc /t:library /r:system.dll /r:system.data.dll /r:system.xml.dll produits.vb dos >vbc /r:produits.dll /r:system.dll /r:system.data.dll /r:system.xml.dll testproduits.vb dos>dir 07/04/2004 04/04/2004 07/04/2004 07/04/2004 03/04/2004

08:40 16:38 08:31 08:40 19:02

7 118 6 5 3

168 784 209 120 312

produits.dll produits.mdb produits.vb testproduits.exe testproduits.vb

puis on teste :
dos>testproduits produits.mdb 1,produit1,10 2,produit2,20 3,produit3,30 ... 1,produit1,10 2,produit2,20 3,produit3,30 8,xxx,1 ... 1,produit1,10 2,produit2,20 3,produit3,30 8,xxx,200 ... 1,produit1,10 2,produit2,20 3,produit3,30 ...

Le lecteur est invit rapprocher la sortie cran ci-dessus du code du programme de test.

3.6 Application web de mise jour de la table des produits en cache


3.6.1 Introduction
Nous crivons maintenant une application web de mise jour de la table des produits (ajout, suppression, modification). La table mise jour restera en mmoire dans un objet [DataTable] et sera partage par tous les utilisateurs. Nous voulons mettre en lumire deux points : la gestion d'un objet [DataTable] les problme de mise jour simultane d'une table par plusieurs utilisateurs. L'architecture MVC de l'application sera la suivante :

Composants serveur - 2

150/192

Client Interface client

Logique applicative [main.aspx] CONTRLEUR Classe d'accs donnes [produits]

Donnes Sources de donnes

VUES

[formulaire] [erreurs]

[produits] [ajout]

3.6.2 Fonctionnement et vues


La vue d'accueil de l'application est la suivante :

5 6 7 8

9 10

vue FORMULAIRE

Cette vue appele [formulaire] permet l'utilisateur de poser une condition de filtrage sur les produits et de fixer le nombre de produits par page qu'il dsire avoir. n
1 2 3 4 5 6 7 8 9 10

nom
lnkFiltre lnkMisaJour lnkAjout panel txtFiltre txtPages rfvLignes rvLignes btnExcuter lblInfo1

type LinkButton LinkButton LinkButton vueFormulaire TextBox TextBox RequiredFieldValidator RangeValidator Label

rle affiche la vue [Formulaire] qui sert fixer la condition de filtrage affiche la vue [Produits] qui sert visualiser et mettre jour la table des produits (modification et suppression) affiche la vue [Ajout] qui sert ajouter un produit la vue [Formulaire] la condition de filtrage nombre de produits par page vrifie la prsence d'une valeur dans [txtPages] vrifie que txtPages est dans l'intervalle [3,10] bouton [submit] qui fait afficher la vue [produits] filtre par la condition (5) Texte d'information en cas d'erreurs

Par exemple, si la vue [formulaire] est remplie de la faon suivante :

Composants serveur - 2

151/192

on obtient le rsultat suivant :

1 2 3

n
1 2 3 4 5 6 7 8

nom
vueProduits rdCroissant rdDcroissant DataGrid1 DataGrid2 LblInfo2 DataGrid3 DataGrid4 DataGrid5

type panel RadioButton DataGrid DataGrid Label DataGrid DataGrid DataGrid

rle permet l'utilisateur de fixer l'ordre du tri dsir lorsqu'il clique sur le titre d'une des colonnes [nom], [prix]. Les deux boutons font partie du groupe [rdTri]. grille d'affichage d'une vue filtre de la table des produits. Le filtre est celui fix par la vue [formulaire]. On a galement .AllowPaging=true, .AllowSorting=true affiche la totalit de la table des produits - permet de suivre les mises jour texte d'information notamment en cas d'erreurs affichera les produits supprims dans la table des produits affichera les produits modifis dans la table des produits affichera les produits ajouts dans la table des produits

Il y a cinq conteneurs de donnes dans cette page. Ils affichent tous la mme table [dtProduits] au travers d'une vue [DataView] diffrente. Une vue reprsente un sous-ensemble des lignes de la table source de la vue. Ce sous-ensemble est cr partir des proprits [RowFilter] et [RowStateFilter] de la classe [DataView] : [RowFilter] permet de fixer un filtre sur les lignes, comme par exemple ci-dessus [prix>30]. Ce type de filtrage sera utilis par [DataGrid1]. [RowStateFilter] permet de fixer un filtre selon l'tat de la ligne de la table. Celui-ci indique l'tat de la ligne par rapport l' tat originel qu'elle avait lorsque la vue sur la table a t cre. Ici la table [dtProduits] provient d'une base de donnes. Initialement toutes ses lignes auront un tat gal [Original] pour indiquer qu'on a affaire aux lignes originelles de la table. Cet tat peut ensuite voluer et prendre diffrentes valeurs dont voici quelques-unes : o [Added] : la ligne a t ajoute - elle ne faisait pas partie de la table d'origine o [Deleted] : la ligne a t supprime - elle est encore dans la table mais "marque" comme " supprimer" o [Modified] : la ligne a t modifie Composants serveur - 2 152/192

[RowStateFilter] permet d'afficher les lignes de la table ayant un certain tat : o [DataViewRowState.Added] : seules les lignes ajoutes sont affiches. Elles le sont avec leurs valeurs actuelles. o [DataViewRowState.ModifiedOriginal] : seules les lignes modifies sont affiches. Elles le sont avec leurs valeurs d'origine. o [DataViewRowState.ModifiedCurrent] : seules les lignes modifies sont affiches. Elles le sont avec leurs valeurs actuelles. o [DataViewRowState.Deleted] : seules les lignes supprimes sont affiches. Elles le sont avec leurs valeurs d'origine. o [DataViewRowState.CurrentRows] : les lignes non supprimes sont affiches. Elles le sont avec leurs valeurs actuelles. Ainsi le filtre [RowStateFilter] aura les valeurs suivantes :
DataGrid1 DataGrid2 DataGrid3 DataGrid4 DataGrid5

pas de filtre sur l'tat des lignes DataViewRowState.CurrentRows DataViewRowState.Deleted DataViewRowState.ModifiedOriginal DataViewRowState.Added

affiche l'tat actuel de la table des produits affiche les lignes supprimes de la table des produits affiche les lignes modifies de la table des produits avec leurs valeurs initiales affiche les lignes ajoutes la table des produits initiale

Les conteneurs [DataGrid1-5] vont nous permettre de suivre la mise jour de la table [dtProduits]. Le composant [DataGrid1] permet la modification et la suppression d'un produit. Nous verrons comment la configuration du composant permet cette mise jour. Il est galement pagin et tri. Ces deux points ont dj t tudis dans un prcdent exemple. Le lien [Ajout] donne accs un formulaire d'ajout de produit :

1 3 5 6

2 4 7 n
1 2 3 4 5 6 7 8

8 type panel TextBox RequiredFieldValidator TextBox RequiredFieldValidator CompareValidator Button Label rle nom du produit vrifie la prsence d'une valeur dans [txtNom] prix du produit vrifie la prsence d'une valeur dans [txtPrix] vrifie que prix>=0 bouton [submit] d'ajout du produit texte d'information sur le rsultat de l'opration d'Ajout

nom
vueAjout txtNom rfvNom txtPrix rfvPrix cvPrix btnAjouter lblInfo3

La base de donnes [produits] peut tre indisponible au dmarrage de l'application. Dans ce cas, la vue [erreurs] est prsente l'utilisateur :

1 2

n
1 2

nom
vueErreurs rptErreurs

type rle panel Repeater liste des erreurs 153/192

Composants serveur - 2

3.6.3 Configuration des conteneurs de donnes


Les cinq conteneurs [DataGrid] ont t configurs sous [WebMatrix]. Ils ont t mis en forme (couleurs et bordures) avec le lien [Configuration automatique] de leur panneau de proprits. Le conteneur [DataGrid1] a t configur l'aide du lien [Gnrateur de proprits] de ce mme panneau. On a autoris le tri (onglet [Gnral]) :

Les colonnes ne sont pas gnres automatiquement contrairement aux quatre autres conteneurs. Elles ont t dfinies la main l'aide de l'assistant :

On a tout d'abord cr deux colonnes de type [Colonne connexe] appeles [nom] et [prix]. Elles ont t associes respectivement aux champs [nom] et [prix] de la source de donnes que les conteneurs afficheront. Voici par exemple, la configuration de la colonne [nom] :

L'expression de tri est l'expression qui devra tre mise derrire la clause [order by] de l'instruction SQL [select] qui sera excute lorsque l'utilisateur cliquera sur le nom de la colonne d'entte [nom] associe au champ [nom] du [DataGrid]. Ici nous avons mis [nom] de sorte que la clause de tri sera [order by nom]. Nous verrons que nous modifierons celle-ci afin qu'elle soit [order by nom asc] ou [order by nom desc] selon l'ordre de tri choisi par l'utilisateur. Nous avons cr par ailleurs, deux colonnes de boutons :

Composants serveur - 2

154/192

La colonne [Modifier, Mettre jour, Annuler] va nous permettre de modifier un produit et la colonne [Supprimer] de le supprimer. Chacune de ces colonnes peut tre configure. La colonne [Modifier, Mettre jour, Annuler] offre la configuration suivante :

On voit qu'on peut modifier les textes des boutons. En fait de boutons, on a le choix entre liens et boutons (liste droulante cidessus). Ce sont les liens qui ont t choisis ici. La configuration de la colonne [Supprimer] est analogue. Outre cet assistant de configuration, nous avons utilis directement la fentre de proprits du [DataGrid] pour renseigner la proprit [DataKeyField] qui indique avec quel champ de la source de donnees, sont indexes les lignes du [DataGrid]. Ici c'est la cl primaire de la table de produits qui est utilise :

Au final, cette configuration gnre le code de prsentation suivant :


<asp:DataGrid id="DataGrid1" runat="server" AllowSorting="True" PageSize="4" AllowPaging="True" AutoGenerateColumns="False" DataKeyField="id"> <SelectedItemStyle ...></SelectedItemStyle> <HeaderStyle ...></HeaderStyle> <FooterStyle ...></FooterStyle> <Columns> <asp:BoundColumn DataField="nom" SortExpression="nom" HeaderText="nom"></asp:BoundColumn> <asp:BoundColumn DataField="prix" SortExpression="prix" HeaderText="prix"></asp:BoundColumn> <asp:EditCommandColumn ButtonType="LinkButton" UpdateText="Mettre &#224; jour" CancelText="Annuler" EditText="Modifier"></asp:EditCommandColumn> <asp:ButtonColumn Text="Supprimer" CommandName="Delete"></asp:ButtonColumn> </Columns> <PagerStyle NextPageText="Suivant" PrevPageText="Pr&#233;c&#233;dent" ...></PagerStyle> </asp:DataGrid>

Comme toujours, une fois une certaine exprience acquise, on peut crire tout ou partie du code ci-dessus directement. Les autres conteneurs [DataGrid] ont la configuration par dfaut obtenue par la gnration automatique des colonnes du [DataGrid] partir de celles de la source de donnes laquelle il est associ. Le conteneur [Repeater] sert afficher une liste d'erreurs. Sa configuration est faite directement dans le code de prsentation :
<asp:Repeater id="rptErreurs" runat="server" EnableViewState="False"> <HeaderTemplate> Les erreurs suivantes se sont produites : <ul> </HeaderTemplate> <ItemTemplate> <li> <%# Container.DataItem %> </li> </ItemTemplate> <FooterTemplate> </ul> </FooterTemplate>

Composants serveur - 2

155/192

</asp:Repeater>

Chaque ligne du composant affiche la valeur [Container.DataItem], c.a.d. la valeur correspondante de la liste de donnes. Celle-ci sera de type [ArrayList] et reprsentera une liste d'erreurs.

3.6.4 Le code de prsentation de l'application


Celui-ci est plac dans le fichier [main.aspx] :
<%@ Page src="main.aspx.vb" inherits="main" autoeventwireup="false" Language="vb" %> <HTML> <HEAD> </HEAD> <body> <form id="Form1" runat="server"> <P> <table> <tr> <td><FONT size="6">Options :</FONT></td> <td> <asp:linkbutton id="lnkFiltre" runat="server" CausesValidation="False"> Filtrage </asp:linkbutton> </td> <td> <asp:linkbutton id="lnkMisajour" runat="server" CausesValidation="False"> Mise jour </asp:linkbutton> </td> <td> <asp:linkbutton id="lnkAjout" runat="server" CausesValidation="False"> Ajout </asp:linkbutton> </td> </tr> </table> </P> <HR width="100%" SIZE="1"> <table> <tr> <td> <asp:panel id="vueFormulaire" runat="server"> <P>Condition de filtrage sur&nbsp;la table LISTE.&nbsp;Exemple : prix&lt;100 and prix&gt;50</P> <P> <asp:TextBox id="txtFiltre" runat="server" Columns="60"></asp:TextBox></P> <P>Nombre de lignes par page : <asp:TextBox id="txtPages" runat="server" Columns="3">5</asp:TextBox> <asp:RequiredFieldValidator id="rfvLignes" runat="server" Display="Dynamic" ControlToValidate="txtPages" ErrorMessage="Indiquez le nombre de lignes par page" EnableClientScript="False"> </asp:RequiredFieldValidator></P> <P> <asp:RangeValidator id="rvLignes" runat="server" Display="Dynamic" ControlToValidate="txtPages" ErrorMessage="Vous devez indiquer un nombre entre 3 et 10" EnableClientScript="False" MaximumValue="10" MinimumValue="3" Type="Integer" EnableViewState="False"> </asp:RangeValidator></P> <P> <asp:Label id="lblinfo1" runat="server"></asp:Label></P> <P> <asp:Button id="btnExcuter" runat="server" CausesValidation="False" EnableViewState="False" Text="Excuter"> </asp:Button></P> </asp:panel> <asp:panel id="vueProduits" runat="server"> <TABLE> <TR> <TD align="center" bgColor="#ff9966"> <P>Tri <asp:RadioButton id="rdCroissant" runat="server" Text="croissant" GroupName="rdTri" Checked="True"> </asp:RadioButton> <asp:RadioButton id="rdDcroissant" runat="server" Text="dcroissant" GroupName="rdTri"> </asp:RadioButton></P> </TD>

Composants serveur - 2

156/192

<TD align="center" bgColor="#ff9966">Tous les produits </TD> <TD align="center" bgColor="#ff9966">Suppressions </TD> <TD align="center" bgColor="#ff9966">Modifications </TD> <TD align="center" bgColor="#ff9966">Ajouts </TD> <TR> <TD vAlign="top"> <P> <asp:DataGrid id="DataGrid1" runat="server" AllowSorting="True" PageSize="4" AllowPaging="True" .... AutoGenerateColumns="False" DataKeyField="id"> <ItemStyle ...></ItemStyle> <HeaderStyle ...></HeaderStyle> <FooterStyle ...></FooterStyle> <Columns> <asp:BoundColumn DataField="nom" SortExpression="nom" HeaderText="nom"> </asp:BoundColumn> <asp:BoundColumn DataField="prix" SortExpression="prix" HeaderText="prix"> </asp:BoundColumn> <asp:EditCommandColumn ButtonType="LinkButton" UpdateText="Mettre &#224; jour" CancelText="Annuler" EditText="Modifier"> </asp:EditCommandColumn> <asp:ButtonColumn Text="Supprimer" CommandName="Delete"> </asp:ButtonColumn> </Columns> <PagerStyle NextPageText="Suivant" PrevPageText="Pr&#233;c&#233;dent" ....> </PagerStyle> </asp:DataGrid> </P> </TD> <TD vAlign="top"> <asp:DataGrid id="DataGrid2" runat="server" ...> <AlternatingItemStyle ...></AlternatingItemStyle> <ItemStyle ...></ItemStyle> <HeaderStyle ....></HeaderStyle> <FooterStyle ...></FooterStyle> <PagerStyle ... Mode="NumericPages"></PagerStyle> </asp:DataGrid> </TD> <TD vAlign="top"> <asp:DataGrid id="DataGrid3" runat="server" ...> <ItemStyle ...></ItemStyle> <HeaderStyle ...></HeaderStyle> <FooterStyle ...></FooterStyle> <PagerStyle ...></PagerStyle> </asp:DataGrid> </TD> <TD vAlign="top"> <asp:DataGrid id="DataGrid4" runat="server" ...> <ItemStyle ....></ItemStyle> <HeaderStyle ....></HeaderStyle> <FooterStyle ...></FooterStyle> <PagerStyle .... Mode="NumericPages"></PagerStyle> </asp:DataGrid> </TD> <TD vAlign="top"> <asp:DataGrid id="DataGrid5" runat="server....> <AlternatingItemStyle ...></AlternatingItemStyle> <HeaderStyle ..></HeaderStyle> <FooterStyle ...></FooterStyle> <PagerStyle ....></PagerStyle> </asp:DataGrid> </TD> </TR> </TABLE> <P></P> <P> <asp:Label id="lblInfo2" runat="server"></asp:Label></P> </asp:panel> <asp:panel id="vueErreurs" runat="server"> <asp:Repeater id="rptErreurs" runat="server" EnableViewState="False"> <HeaderTemplate> Les erreurs suivantes se sont produites : <ul> </HeaderTemplate> <ItemTemplate> <li> <%# Container.DataItem %>

Composants serveur - 2

157/192

</li> </ItemTemplate> <FooterTemplate> </ul> </FooterTemplate> </asp:Repeater> </asp:panel> <asp:panel id="vueAjout" EnableViewState="False" Runat="server"> <P>Ajout d'un produit</P> <P> <TABLE ... border="1"> <TR> <TD>nom</TD> <TD> <asp:TextBox id="txtNom" runat="server" Columns="30"></asp:TextBox> <asp:RequiredFieldValidator id="rfvNom" runat="server" ControlToValidate="txtNom" ErrorMessage="Vous devez indiquer un nom"> </asp:RequiredFieldValidator> </TD> </TR> <TR> <TD>prix</TD> <TD> <asp:TextBox id="txtPrix" runat="server" Columns="10"></asp:TextBox> <asp:RequiredFieldValidator id="rfvPrix" runat="server" ControlToValidate="txtPrix" ErrorMessage="Vous devez indiquer un prix"> </asp:RequiredFieldValidator> <asp:CompareValidator id="cvPrix" runat="server" ControlToValidate="txtPrix" ErrorMessage="CompareValidator" Type="Double" Operator="GreaterThanEqual" ValueToCompare="0"> </asp:CompareValidator> </TD> </TR> </TABLE> </P> <P> <asp:Button id="btnAjouter" runat="server" CausesValidation="False" Text="Ajouter"> </asp:Button></P> <P> <asp:Label id="lblInfo3" runat="server"></asp:Label></P> </asp:panel> </td> </tr> </table> </form> </body> </HTML>

Notons les quelques points suivants : la page est compose de quatre conteneurs (panel) [vueFormulaire, vueProduits, vueAjout, vueErreurs] qui vont former les quatre vues de l'application. les boutons ou liens de type [submit] ont la proprit [CausesValidation=false]. La proprit [causesValidation=true] provoque l'excution de tous les contrles de validation de la page. Or ici, tous les contrles de validit ne sont pas faire en mme temps. Lorsqu'on fait un ajout par exemple, on ne veut pas que les contrles concernant le nombre de lignes par page soient excuts. On prcisera donc nous-mmes quels contrles de validit doivent tre faits.

3.6.5 Le code de contrle [global.asax]


Le contrleur [global.asax] est le suivant :
<%@ Application src="global.asax.vb" inherits="Global" %>

Le code associ [global.asax.vb] :


Imports Imports Imports Imports Imports Imports Imports Imports System System.Web System.Web.SessionState st.istia.univangers.fr System.Configuration System.Data Microsoft.VisualBasic System.Collections

Composants serveur - 2

158/192

Public Class Global Inherits System.Web.HttpApplication Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs) ' on rcupre les informations de configuration Dim chanedeConnexion As String = ConfigurationSettings.AppSettings("OLEDBStringConnection") Dim defaultProduitsPage As String = ConfigurationSettings.AppSettings("defaultProduitsPage") Dim erreurs As New ArrayList If IsNothing(chanedeConnexion) Then erreurs.Add("Le paramtre [OLEDBStringConnection] n'a pas t initialis") If IsNothing(defaultProduitsPage) Then erreurs.Add("Le paramtre [defaultProduitsPage] n'a pas t initialis") Else Try Dim defProduitsPage As Integer = CType(defaultProduitsPage, Integer) If defProduitsPage <= 0 Then Throw New Exception Catch ex As Exception erreurs.Add("Le paramtre [defaultProduitsPage] a une valeur incorrecte") End Try End If ' des erreurs de configuration ? If erreurs.Count <> 0 Then ' on note les erreurs Application("erreurs") = erreurs ' on quitte Exit Sub End If ' ici pas d'erreurs de configuration ' on cre un objet produits Dim dtProduits As DataTable Try dtProduits = New produits(chanedeConnexion).getProduits Catch ex As ExceptionProduits 'il y a eu erreur d'accs aux produits, on le note dans l'application Application("erreurs") = ex.erreurs Exit Sub Catch ex As Exception ' erreur non gre erreurs.Add(ex.Message) Application("erreurs") = erreurs ' exit sub End Try ' ici pas d'erreurs d'initialisation ' on mmorise le nbre de produits par page Application("defaultProduitsPage") = defaultProduitsPage ' on mmorise la table des produits Application("dtProduits") = dtProduits End Sub Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs) ' init des variables de session If IsNothing(Application("erreurs")) Then ' vue sur la table des produits Session("dvProduits") = CType(Application("dtProduits"), DataTable).DefaultView ' nbre de produits par page Session("nbProduitsPage") = Application("defaultProduitsPage") ' page courante affiche Session("pageCourante") = 0 End If End Sub End Class

Dans [Application_Start], on commence par rcuprer deux informations du fichier de configuration [web.config] de l'application : OLEDBStringConnection : la chane OLEDB de connexion la base des produits defaultProduitsPage : le nombre par dfaut de produits par page affiche
<?xml version="1.0" encoding="utf-8" ?> <configuration> <appSettings> <add key="OLEDBStringConnection" value="Provider=Microsoft.Jet.OLEDB.4.0; Ole DB Services=-4; Data Source=D:\data\devel\aspnet\poly\webforms3\vs\majproduits1\produits.mdb" /> <add key="defaultProduitsPage" value="5" /> </appSettings> </configuration>

Si l'une de ces deux informations est absente, une liste d'erreurs est alimente et mise dans l'application. Il en est de mme si le paramtre [defaultProduitsPage] existe mais est incorrect. Si les deux paramtres attendus sont prsents et corrects, une table [dtProduits] est construite et mise dans l'application. C'est cette table qui sera exploite et mise jour par les diffrents clients. La Composants serveur - 2 159/192

base de donnes elle, restera inchange. On s'intressera la mise jour de celle-ci dans une prochaine application. Cette table est construite partir d'une instance de la classe [produits] tudie prcdemment et de la mthode [getProduits] de celle-ci. L'obtention de la table [dtProduits] peut chouer. Dans ce cas, on sait que la classe [produits] lance une exception de type [ExceptionProduits]. Elle est ici intercepte et la liste d'erreurs associe est mise dans l'application associe la cl [erreurs]. La prsence de cette cl dans les informations enregistres dans l'application sera teste chaque traitement de requte. Si elle est trouve, la vue [erreurs] sera envoye au client. Si la table [dtProduits] est partage par tous les clients web, chacun d'eux aura nanmoins sa propre vue [dvProduits] sur celle-ci. En effet, chaque client web a la possibilit de fixer un filtre sur la table [dtProduits] ainsi qu'un ordre de tri. Ces informations propres chaque client web sont stockes dans la vue du client. Aussi celle-ci est-elle cre dans [Session_Start] afin d'tre place dans la session propre chaque client. On utilise l'attribut [DefaultView] de la table [dtProduits] pour avoir une vue par dfaut sur la table. Au dpart, il n'y a ni filtre ni ordre de tri. Par ailleurs, deux informations sont galement mises dans la session : le nombre de produits par page de cl [nbProduitsPage]. Au dmarrage de la session, ce nombre est gal au nombre par dfaut dfini dans le fichier de configuration. le numro de la page courante des produits. Au dpart, c'est la premire page.

3.6.6 Le code de contrle [main.aspx.vb]


Le squelette du contrleur est le suivant :
Imports Imports Imports Imports Imports Imports Imports System.Collections Microsoft.VisualBasic System.Data st.istia.univangers.fr System System.Xml System.Web.UI.WebControls

Public Class main Inherits System.Web.UI.Page ' composants page Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents Protected WithEvents ' donnes Protected Protected Protected Protected

txtPages As System.Web.UI.WebControls.TextBox btnExcuter As System.Web.UI.WebControls.Button vueFormulaire As System.Web.UI.WebControls.Panel DataGrid1 As System.Web.UI.WebControls.DataGrid lnkErreurs As System.Web.UI.WebControls.LinkButton vueErreurs As System.Web.UI.WebControls.Panel rdCroissant As System.Web.UI.WebControls.RadioButton rdDcroissant As System.Web.UI.WebControls.RadioButton txtFiltre As System.Web.UI.WebControls.TextBox lblInfo2 As System.Web.UI.WebControls.Label lblinfo1 As System.Web.UI.WebControls.Label lblErreurs As System.Web.UI.WebControls.Label DataGrid2 As System.Web.UI.WebControls.DataGrid vueProduits As System.Web.UI.WebControls.Panel vueAjout As System.Web.UI.WebControls.Panel lnkMisajour As System.Web.UI.WebControls.LinkButton lnkFiltre As System.Web.UI.WebControls.LinkButton txtNom As System.Web.UI.WebControls.TextBox txtPrix As System.Web.UI.WebControls.TextBox btnAjouter As System.Web.UI.WebControls.Button lblInfo3 As System.Web.UI.WebControls.Label rfvLignes As System.Web.UI.WebControls.RequiredFieldValidator rvLignes As System.Web.UI.WebControls.RangeValidator rfvNom As System.Web.UI.WebControls.RequiredFieldValidator rfvPrix As System.Web.UI.WebControls.RequiredFieldValidator cvPrix As System.Web.UI.WebControls.CompareValidator lnkAjout As System.Web.UI.WebControls.LinkButton DataGrid3 As System.Web.UI.WebControls.DataGrid DataGrid4 As System.Web.UI.WebControls.DataGrid DataGrid5 As System.Web.UI.WebControls.DataGrid

page dtProduits As DataTable dvProduits As DataView defaultProduitsPage As Integer erreur As Boolean = False

' chargement page Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ... End Sub Private Sub afficheErreurs()

Composants serveur - 2

160/192

... End Sub Private Sub afficheFormulaire() ... End Sub Private Sub btnExcuter_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnExcuter.Click .... End Sub Private Sub afficheProduits(ByVal page As Integer, ByVal taillePage As Integer) ... End Sub Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) Handles DataGrid1.PageIndexChanged ... End Sub Private Sub DataGrid1_SortCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles DataGrid1.SortCommand .... End Sub Private Sub DataGrid1_DeleteCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles DataGrid1.DeleteCommand .... End Sub Private Sub DataGrid1_EditCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles DataGrid1.EditCommand ... End Sub Private Sub DataGrid1_CancelCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles DataGrid1.CancelCommand ... End Sub Private Sub DataGrid1_UpdateCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles DataGrid1.UpdateCommand ... End Sub Private Sub supprimerProduit(ByVal idProduit As Integer) ... End Sub Private Sub modifierProduit(ByVal idProduit As Integer, ByVal item As DataGridItem) .... End Sub Private Sub lnkMisajour_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkMisajour.Click ... End Sub Private Sub lnkAjout_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkAjout.Click ... End Sub Private Sub afficheAjout() ... End Sub Private Sub lnkFiltre_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkFiltre.Click .... End Sub Private Sub btnAjouter_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAjouter.Click .... End Sub End Class

Composants serveur - 2

161/192

3.6.7 Les donnes d'instance


La classe [main] utilise les donnes d'instance suivantes :
Public Class main Inherits System.Web.UI.Page ' composants page Protected WithEvents txtPages As System.Web.UI.WebControls.TextBox ... ' donnes Protected Protected Protected page dtProduits As DataTable dvProduits As DataView defaultProduitsPage As Integer

dtProduits dvProduits defaultProduitsPage

la table [DataTable] des produits - commune tous les clients la vue [DataView] des produits - propre chaque client nbre de produits par page propos par dfaut

3.6.8 La procdure [Page_Load] de chargement de la page


Le code de la procdure [page_Load] est le suivant :
' chargement page Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ' on regarde si l'application est en erreur If Not IsNothing(Application("erreurs")) Then ' l'application ne s'est pas initialise correctement afficheErreurs(CType(Application("erreurs"), ArrayList)) Exit Sub End If ' on rcupre une rfrence sur la table des produits dtProduits = CType(Application("dtProduits"), DataTable) ' on rcupre la vue sur les produits dvProduits = CType(Session("dvProduits"), DataView) ' on rcupre le nbre de produits par page defaultProduitsPage = CType(Application("defaultProduitsPage"), Integer) ' on annule une ventuelle mise jour en cours DataGrid1.EditItemIndex = -1 '1re requte If Not IsPostBack Then ' on affiche le formulaire initial txtPages.Text = defaultProduitsPage.ToString afficheFormulaire() End If End Sub

on commence tout d'abord par vrifier si la table des produits a pu tre charge au dmarrage de l'application. Si ce n'est pas le cas, on affiche la vue [erreurs] avec les messages d'erreurs appropris puis on quitte la procdure aprs avoir positionn le boolen [erreur] vrai. Cet indicateur sera test par certaines procdures. on rcupre la table des produits [dtProduits] dans l'application et on la met dans la variable d'instance [dtProduits] afin qu'elle soit partage par toutes les mthodes de la page. on fait de mme avec la vue [dvProduits] rcupre elle dans la session et le nombre par dfaut de produits par page rcupr lui dans l'application. si c'est la 1re requte du client, on lui prsente le formulaire de dfinition des conditions de filtrage et de pagination avec une pagination par dfaut de [defaultProduitsPage] produits par page. on annule le mode dition du composant [dataGrid1]. Ce composant a un attribut [EditItemIndex] qui est l'index de l'lment en cours de modification. Si [EditItemIndex]=-1, alors il n'y a aucun lment en cours de modification. Si [EditItemIndex]=i, alors l'lment n i du composant [DataGrid] est en cours d'dition. Il est alors prsent diffremment des autres lments du [dataGrid] :

Composants serveur - 2

162/192

Ci-dessus, l'lment [Produit8, 80] est en mode [dition]. On voit ci-dessus que l'utilisateur peut valider sa mise jour par le lien [Mettre jour], l'annuler par le lien [Annuler]. Il peut aussi utiliser d'autres liens n'ayant rien voir avec l'lment en cours d'dition. En mettant [DataGrid1.EditItemIndex=-1] chaque chargement de la page, on s'assure d'annuler systmatiquement le mode dition de [DataGrid1]. On ne lui affectera une valeur diffrente que si un lien [Modifier] a t cliqu. On le fera dans la procdure traitant cet vnement. On aura les situations suivantes : 1. le lien [Modifier] de l'lment n 8 de [DataGrid1] a t cliqu. [Page_Load] met tout d'abord -1 dans [DataGrid1.EditItemIndex]. Puis la procdure traitant l'vnement [Modifier] mettra 8 dans [DataGrid1.EditItemIndex]. Au final, lorsque la page sera renvoye au client, l'lment n 8 sera bien en mode [dition] et celui-ci apparatra comme montr ci-dessus. l'utilisateur modifie le produit qui est en mode [dition] et valide sa modification par le lien [Mettre jour]. [Page_Load] met -1 dans [DataGrid1.EditItemIndex]. Puis la procdure traitant l'vnement [Mettre jour] s'excutera et l'lment n 8 de [dtProduits] sera mis jour. Lorsque la page sera renvoye au client, aucun lment de [DataGrid1] ne sera en mode dition (DataGrid.EditItemIndex=-1). Si un utilisateur est entr en mise jour d'un produit, il serait cohrent qu'il abandonne cette mise jour par [Annuler]. Cependant rien ne l'empche cliquer sur un autre lien. Il nous faut donc prvoir ce cas. Dans ce cas comme dans les prcdents, [Page_Load] commence par mettre -1 dans [DataGrid1.EditItemIndex], puis la procdure traitant l'vnement qui s'est produit s'excutera. Elle ne modifiera pas la proprit [DataGrid1.EditItemIndex] qui restera donc avec la valeur -1. Lorsque la page sera renvoye au client, aucun lment de [DataGrid1] ne sera en mode dition.

2.

3.

On voit qu'en mettant [EditItemIndex] -1 au chargement de la page, on vite d'avoir se proccuper si l'utilisateur tait ou non en mise jour lorsqu'il a cliqu sur un lien.

3.6.9 Affichage des vues [erreurs], [formulaire] et [ajout]


L'affichage de la vue [erreurs] est demande au chargement de la page [Page_Load] si on dcouvre que l'application s'est mal initialise. Cet affichage se fait en associant le composant de donnes [rptErreurs] la liste d'erreurs passe en paramtres et en rendant visible le conteneur appropri. Enfin, les trois liens [Filtre, Mise jour, Ajout] sont cachs car en cas d'erreur l'application ne peut tre utilise.
Private Sub afficheErreurs(ByVal erreurs As ArrayList) ' on associe la liste d'erreurs au rpteur rptErreurs With rptErreurs .DataSource = erreurs .DataBind() End With 'on inhibe les options lnkAjout.Visible = False lnkMisajour.Visible = False lnkFiltre.Visible = False ' on affiche la vue [erreurs] vueErreurs.Visible = True vueFormulaire.Visible = False vueProduits.Visible = False vueAjout.Visible = False End Sub

Les autres vues sont demandes partir des options proposes l'utilisateur :

L'affichage de la vue [Ajout] est demand lorsque l'utilisateur clique sur le lien [Ajout] de la page : Composants serveur - 2 163/192

Private Sub lnkAjout_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkAjout.Click ' on affiche la vue [ajout] afficheAjout() End Sub Private Sub afficheAjout() ' affiche la vue ajout vueAjout.Visible = True vueFormulaire.Visible = False vueProduits.Visible = False vueErreurs.Visible = False End Sub

L'affichage de la vue [Formulaire] est demand lorsque l'utilisateur clique sur le lien [Filtrage] de la page. On doit alors prsenter la vue permettant de dfinir le filtre sur la table des produits le nombre de produits prsents dans chaque page web On prsente un formulaire reprenant les valeurs stockes dans la session :
Private Sub lnkFiltre_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkFiltre.Click ' dfinir les conditions de filtrage txtFiltre.Text = dvProduits.RowFilter ' dfinir la pagination txtPages.Text = CType(Session("nbProduitsPage"), String) ' on affiche le formulaire de filtrage afficheFormulaire() End Sub

La condition de filtrage d'une vue est dfinie dans son attribut [RowFilter]. Rappelons que la vue filtr et pagine s'appelle [dvProduits] et a t rcupre dans la session au chargement de la page [Page_Load]. La condition de filtrage est donc rcupre dans [dvProduits.RowFilter]. Le nombre de produits par page est galement rcupr dans la session. Si l'utilisateur n'a jamais dfini cette information, ce nombre est gal au nombre par dfaut de produits par page. Ceci fait, on affiche le formulaire permettant de dfinir ces deux informations :
Private Sub afficheFormulaire() ' on affiche la vue [formulaire] vueFormulaire.Visible = True vueErreurs.Visible = False vueProduits.Visible = False vueAjout.Visible = False End Sub

3.6.10 Validation de la vue [formulaire]


Le clic sur le bouton [Excuter] ci-dessus est trait par la procdure suivante :
Private Sub btnExcuter_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnExcuter.Click ' page valide ? rfvLignes.Validate() rvLignes.Validate() If Not rfvLignes.IsValid Or Not rvLignes.IsValid Then afficheFormulaire() Exit Sub End If ' attachement des donnes filtres la grille Try dvProduits.RowFilter = txtFiltre.Text.Trim Catch ex As Exception

Composants serveur - 2

164/192

lblinfo1.Text = "Erreur de filtrage (" + ex.Message + ")" afficheFormulaire() Exit Sub End Try ' on mmorise le nbre de produits par page Session("nbProduitsPage") = txtPages.Text 'et la page courante Session("pageCourante") = 0 ' tout va bien - on affiche les donnes afficheProduits(0, CType(txtPages.Text, Integer)) End Sub

Rappelons tout d'abord que la page a deux composants de validation : n


7 8

nom
rfvLignes rvLignes

type RequiredFieldValidator RangeValidator

rle vrifie la prsence d'une valeur dans [txtPages] vrifie que txtPages est dans l'intervalle [3,10]

La procdure commence par faire excuter le code de contrle des deux composants ci-dessus l'aide de leur mthode [Validate] puis vrifie la valeur de leur attribut [IsValid]. Celui-ci est [true] uniquement si la donne vrifie a t trouve valide. Si les tests de validit d'un des deux composants a chou, on raffiche la vue [formulaire] afin que l'utlisateur corrige son ou ses erreurs. La condition de filtrage est applique l'attribut [dvProduits.RowFilter]. Ici une exception peut se produire si l'utilisateur a mis un critre de filtrage incorrect comme il est montr ci-dessous :

Dans ce cas, la vue [formulaire] est raffiche. Si les deux informations saisies sont correctes, alors deux donnes sont places dans la session : le nombre de produits par page choisi par l'utilisateur le n de la page qui va tre affiche - au dpart la page 0 Ces deux informations sont utilises chaque fois que la vue [produits].

3.6.11 Affichage de la vues [produits]


La vue [produits] est la suivante :

Composants serveur - 2

165/192

Nous avons 5 composants [DataGrid] exposant chacun une vue particulire de la table des produits [dtProduits]. En partant de la gauche : nom
DataGrid1 DataGrid2 DataGrid3 DataGrid4 DataGrid5

rle grille d'affichage d'une vue filtre de la table des produits. Le filtre est celui fix par la vue [formulaire]. On a galement .AllowPaging=true, .AllowSorting=true affiche la totalit de la table des produits - permet de suivre les mises jour affichera les produits supprims dans la table des produits affichera les produits modifis dans la table des produits affichera les produits ajouts dans la table des produits

La procdure [afficheProduits] est charge d'afficher la vue prcdente :


Private Sub afficheProduits(ByVal page As Integer, ByVal taillePage As Integer) ' on attache les donnes aux deux composants [DataGrid] Application.Lock() With DataGrid1 .DataSource = dvProduits .PageSize = taillePage .CurrentPageIndex = page .DataBind() End With Dim dvCurrent As New DataView(dtProduits) dvCurrent.RowStateFilter = DataViewRowState.CurrentRows With DataGrid2 .DataSource = dvCurrent .DataBind() End With Dim dvSupp As DataView = New DataView(dtProduits) dvSupp.RowStateFilter = DataViewRowState.Deleted With DataGrid3 .DataSource = dvSupp .DataBind() End With Dim dvModif As DataView = New DataView(dtProduits) dvModif.RowStateFilter = DataViewRowState.ModifiedOriginal With DataGrid4 .DataSource = dvModif .DataBind() End With Dim dvAjout As DataView = New DataView(dtProduits) dvAjout.RowStateFilter = DataViewRowState.Added With DataGrid5 .DataSource = dvAjout .DataBind() End With Application.UnLock() ' on affiche la vue [produits] vueProduits.Visible = True vueFormulaire.Visible = False vueErreurs.Visible = False vueAjout.Visible = False ' on mmorise la page courante Session("pageCourante") = page End Sub

La procdure admet deux paramtres : le n de page [page] afficher dans [DataGrid1] le nombre de produits [taillePage] par page La liaison des [DataGrid] la table des produits se fait au travers de 5 vues diffrentes. [DataGrid1] est lie la vue pagine et trie [dvProduits].
With DataGrid1 .DataSource = dvProduits .PageSize = taillePage .CurrentPageIndex = page .DataBind() End With

C'est lors de cette liaison de [DataGrid1] sa source de donnes qu'on a besoin des deux informations passes en paramtres la procdure. [DataGrid2] est li une vue affichant tous les lments actuels de la table [dtProduits]. Elle prsente comme [DataGrid1] une vue actualise de la table [dtProduits] mais sans pagination ni tri. 166/192

Composants serveur - 2

Dim dvCurrent As New DataView(dtProduits) dvCurrent.RowStateFilter = DataViewRowState.CurrentRows With DataGrid2 .DataSource = dvCurrent .DataBind() End With

Nous utilisons ici l'attribut [RowStateFilter] de la classe [DataView]. Une ligne d'une table ou ici d'une vue possde un attribut [RowState] indiquant l'tat de la ligne. En voici quelques-uns : o [Added] : la ligne a t ajoute o [Modified] : la ligne a t modifie o [Deleted] : la ligne a t dtruite o [Unchanged] : la ligne n'a pas chang Lorsque la table [dtProduits] a t construite initialement dans [Application_Start], toutes ses lignes taient dans l'tat [Unchanged]. Cet tat va voluer au fil des mises jour des clients web : lorsqu'un client cre un nouveau produit, une ligne est ajoute la table [dtProduits]. Elle sera dans l'tat [Added]. lorsqu'un produit est modifi, son tat passe de [Unchanged] [Modified]. lorsqu'un produit est supprim, la ligne n'est pas supprime physiquement. Elle est plutt marque comme tant supprimer et son tat passe [Deleted]. Il est possible d'annuler cette suppression. L'attribut [RowStateFilter] de la classe [DataView] permet de filtrer une vue selon l'tat [RowState] de ses lignes. Ses valeurs possibles sont celles de l'numration [DataRowViewState] : DataRowViewState.CurrentRows : les lignes non supprimes sont affiches avec leurs valeurs actuelles DataRowViewState.Added : les lignes ajoutes sont affiches avec leurs valeurs actuelles DataRowViewState.Deleted : les lignes supprimes sont affiches avec leurs valeurs d'origine DataRowViewState.ModifiedOriginal : les lignes modifies sont affiches avec leurs valeurs d'origine DataRowViewState.ModifiedCurrent : les lignes modifies sont affiches avec leurs valeurs actuelles Une ligne modifie a deux valeurs : la valeur d'origine qui est celle que la ligne avait avant la premire modification et la valeur actuelle celle obtenue aprs une ou plusieurs modifications. Ces deux valeurs sont conserves simultanment. Les DataGrid 2 5 affichent une vue de la table [dtProduits] filtre par l'attribut [RowStateFilter] DataGrid
DataGrid2 DataGrid3 DataGrid4 DataGrid5

Filtre RowStateFilter= DataRowViewState.CurrentRows RowStateFilter= DataRowViewState.Deleted RowStateFilter= DataRowViewState.ModifiedOriginal RowStateFilter= DataRowViewState.Added

La table [dtPoduits] est mise jour simultanment par diffrents clients web, ce qui peut amener des conflits d'accs la table. Lorsqu'un client affiche ses vues de la table [dtProduits], on veut viter qu'il le fasse alors que la table est dans un tat instable parce qu'en cours de modification par un autre client web. Aussi chaque fois qu'un client a besoin de la table [dtProduits] en lecture comme ci-dessus ou en criture lorsqu'il va ajouter, modifier et supprimer des produits, il se synchronisera avec les autres clients au moyen de la squence suivante :
Application.Lock ... section critique 1 Application.Unlock

Il peut exister plusieurs sections critiques dans le code de l'application :


Application.Lock ... section critique 2 Application.Unlock

La mcanique est la suivante : 1. un ou plusieurs clients arrivent l'instruction [Application.Lock] de la section critique 1. C'est un distributeur de jeton d'entre unique. Un unique client btient ce jeton. Appelons-le C1. 2. tant que le client C1 n'a pas rendu le jeton par [Application.Unlock], aucun autre client n'est autoris entrer dans une section critique contrle par [Application.Lock]. Dans l'exemple ci-dessus, aucun client ne peut donc entrer dans les sections critiques 1 et 2. 3. le client C1 excute [Application.Unlock] et rend donc le jeton d'entre. Celui-ci peut alors tre donn un autre client. Les tapes 1 3 sont rejoues. Avec ce mcanisme, nous assurerons qu'un seul client a accs la table [dtProduits] que ce soit en lecture ou criture. Composants serveur - 2 167/192

Le lien [Mise jour] affiche galement la vue [Produits] :

La procdure associe est la suivante :


Private Sub lnkMisajour_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkMisajour.Click ' affichage vue produits afficheProduits(CType(Session("pageCourante"), Integer), CType(Session("nbProduitsPage"), Integer)) End Sub

On doit afficher la vue [produits]. Ceci est fait l'aide de la procdure [afficheProduits] qui admet deux paramtres : le n de la page courante afficher et le nombre de produits par page afficher. Ces deux informations sont dans la session, ventuellement avec leurs valeurs initiales si l'utilisateur ne les a jamais dfinies lui-mme.

3.6.12 Pagination et tri de [DataGrid1]


Nous avons dj rencontr les procdures grant la pagination et le tri d'un [DataGrid] dans un autre exemple. Dans le cas d'un changement de page, on affiche la vue [produits] en passant le n de la nouvelle page en paramtre la procdure [afficheProduits].
Private Sub DataGrid1_PageIndexChanged(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridPageChangedEventArgs) Handles DataGrid1.PageIndexChanged ' chgt de page afficheProduits(e.NewPageIndex, DataGrid1.PageSize) End Sub

La procdure [DataGrid1_SortCommand] est excute lorsque l'utilisateur clique sur l'entte de l'une des colonnes de [DataGrid1]. Il faut alors affecter l'attribut [Sort] de la vue [dvProduits] affiche par [DataGrid1] l'expression de tri. Celle-ci est syntaxiquement quivalente l'expression de tri place derrire la clause [order by] de l'instruction SQL SELECT. L'argument [e] de la procdure a un attribut [SortExpression] qui nous donne l'expression de tri associe la colonne dont l'entte a t cliqu. Lorsque le composannt [DataGrid1] a t construit, cette expression de tri avait t dfinie. Ci-dessous, celle dfinie pour la colonne [nom] de [DataGrid1] :

Si l'expression de tri n'a pas t dfinie lors de la conception du [DataGrid], c'est le nom du champ de donnes associ la colonne du [DataGrid] qui est utilise. Le sens du tri (croissant, dcroissant) est ici fix par l'utilisateur l'aide des boutons radio [rdCroissant, rd Dcroissant] :

La procdure de tri est la suivante :


Private Sub DataGrid1_SortCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridSortCommandEventArgs) Handles DataGrid1.SortCommand ' on trie le dataview With dvProduits .Sort = e.SortExpression + " " + CType(IIf(rdCroissant.Checked, "asc", "desc"), String) End With ' on l'affiche afficheProduits(0, DataGrid1.PageSize) End Sub

Composants serveur - 2

168/192

3.6.13 Suppression d'un produit


Pour supprimer un produit, l'utilisateur utilise le lien [Supprimer] de la ligne du produit :

La procdure [DataGrid1_DeleteCommand] est alors excute :


Private Sub DataGrid1_DeleteCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles DataGrid1.DeleteCommand ' suppression d'un produit ' cl du produit supprimer Dim idProduit As Integer = CType(DataGrid1.DataKeys(e.Item.ItemIndex), Integer) ' suppression du produit Dim erreur As Boolean = False Try supprimerProduit(idProduit) Catch ex As Exception ' pb lblInfo2.Text = ex.Message erreur = True End Try ' chgt de page ? Dim page As Integer = DataGrid1.CurrentPageIndex If Not erreur AndAlso DataGrid1.Items.Count = 1 Then page = DataGrid1.CurrentPageIndex - 1 If page < 0 Then page = 0 End If ' affichage produits afficheProduits(page, DataGrid1.PageSize) End Sub

La mise jour des produits se fera via la cl [id] du produit. En effet la colonne [id] de la table [dtProduits] est cl primaire et grce elle, nous pouvons retrouver dans la table [dtProduits] la ligne du produit qui est mis jour dans le composant [DataGrid1]. La procdure commence donc par rcuprer la cl du produit dont le lien [Supprimer] a t cliqu :
' cl du produit supprimer Dim idProduit As Integer = CType(DataGrid1.DataKeys(e.Item.ItemIndex), Integer)

Nous savons que [e.Item] est l'lment de [DataGrid1] l'origine de l'vnement. Grosso-modo cet lment est une ligne du [DataGrid]. [e.Item.ItemIndex] est le n de la ligne l'origine de l'vnement. Cet index est relatif la page courante affiche. Ainsi la premire ligne de la page affiche la proprit [ItemIndex=0] mme si elle a le numro 17 dans la table des produits. [DataGrid1.DataKeys] est la liste des cls du [DataGrid]. Parce que nous avons, la conception, crit [DataGrid1.DataKey=id], les cls de [DataGrid1] sont constitues des valeurs de la colonne [id] de la table [dtProduits] qui est en mme temps cl primaire. Ainsi [DataGrid1.DataKeys(e.Item.ItemIndex)] est la cl [id] du produit supprimer. Ceci obtenu, on demande sa suppression l'aide de la procdure [supprimerProduit] :
' suppression du produit Dim erreur As Boolean = False Try supprimerProduit(idProduit) Catch ex As Exception ' pb lblInfo2.Text = ex.Message erreur = True End Try

Cette suppression peut chouer dans certains cas. Nous verrons pourquoi. Une exception est alors lance. Elle est ici gre. S'il y a erreur, le [DataGrid1] n'a pas changer. Seul un message d'erreur est ajout la vue [produits]. S'il n'y a pas d'erreur et si l'utilisateur vient de supprimer l'unique produit de la page courante alors on affiche la page prcdente. Composants serveur - 2 169/192

' chgt de page ? Dim page As Integer = DataGrid1.CurrentPageIndex If Not erreur AndAlso DataGrid1.Items.Count = 1 Then page = DataGrid1.CurrentPageIndex - 1 If page < 0 Then page = 0 End If

Dans tous les cas, la vue [produits] est raffiche avec la procdure [afficheProduits] :
' affichage produits afficheProduits(page, DataGrid1.PageSize)

La procdure [supprimerProduit] est la suivante :


Private Sub supprimerProduit(ByVal idProduit As Integer) Dim erreur As String Try ' synchronisation Application.Lock() ' recherche de la ligne supprimer Dim ligne As DataRow = dtProduits.Rows.Find(idProduit) If ligne Is Nothing Then erreur = String.Format("Produit [{0}] inexistant", idProduit) Else ' suppression de la ligne ligne.Delete() End If Catch ex As Exception erreur = String.Format("Erreur de suppression : {0}", ex.Message) Finally ' fin de synchronisation Application.UnLock() End Try ' on lance une exception si erreur If erreur <> String.Empty Then Throw New Exception(erreur) End Sub

La procdure reoit en paramtre la cl du produit supprimer. S'il n'y avait qu'un client faire les mises jour, cette suppression pourrait se faire comme suit :
' suppression de la ligne [idProduit] dtProduits.Rows.Find(idProduit).Delete()

Avec plusieurs clients faisant les mises jour en mme temps, c'est plus compliqu. En effet, considrons la squence dans le temps suivante :

1 Temps T1 T2 T3 T4

Action le client A lit la table [dtProduits] - il y a un produit de cl 20 le client B lit la table [dtProduits] - il y a un produit de cl 20 le client A supprime le produit de cl 20 le client B supprime le produit de cl 20

Lorsque les clients A et B lisent la table des produits et qu'ils en affichent le contenu dans une page web, la table contient le produit de cl 20. Ils peuvent donc vouloir faire une action sur ce produit, par exemple le dtruire. Il y en forcment un des deux qui le fera le premier. Celui qui passera aprs voudra alors dtruire un produit qui n'existe plus. Ce cas est pris en compte par la procdure [supprimerProduits] de plusieurs faons : il y a tout d'abord synchronisation avec [Application.Lock]. Cela signifie que lorsqu'un client passe cette barrire, il n'y en a pas d'autre modifier ou lire la table des produits. En effet ces oprations sont toutes synchronises de cette faon. Nous l'avons dj vu pour la lecture. on vrifie ensuite si le produit qu'on veut supprimer existe. Si oui, il est supprim, sinon un message d'erreur est prpar.
' recherche de la ligne supprimer Dim ligne As DataRow = dtProduits.Rows.Find(idProduit) If ligne Is Nothing Then erreur = String.Format("Produit [{0}] inexistant", idProduit) Else ' suppression de la ligne ligne.Delete()

Composants serveur - 2

170/192

End If

on sort de la squence critique par [Application.Unlock] afin de permettre aux autres clients de faire leurs mises jour. si la ligne n'a pas pu tre supprime, la procdure lance une exception associe un message d'erreur.

Voyons un exemple. Nous lanons un client [Mozilla] et nous prenons immdiatement l'option [Mise jour] (vue partielle) :

Nous faisons de mme avec un client [Internet Explorer] (vue partielle) :

Avec le client [Mozilla] nous supprimons le produit [produit2]. Nous obtenons la nouvelle page suivante :

L'opration de suppression s'est bien droule. Le produit supprim apparat bien le [DataGrid] des produits supprims et n'apprat plus dans la liste des produits des composants [DataGrid1] et [DataGrid2] qui exposent les produits actuellement prsents dans la table. Faisons de mme avec [Interbet Explorer]. On supprime l'lment [produit2] :

Composants serveur - 2

171/192

La rponse obtenue est la suivante :

Un message d'erreur signale l'utilisateur que le produit de cl [2] n'existe pas. La rponse renvoie au client une nouvelle vue reltant l'tat actuel de la table. On voit qu'effectivement le produit de cl [2] est dans la liste des produits supprims. La vue [produits] reflte donc les mises jour de tous les clients et non d'un seul.

3.6.14 Ajout d'un produit


Pour ajouter un produit, l'utilisateur utilise le lien [Ajout] des options. Celui-ci se contente d'afficher la vue [Ajout] :

Composants serveur - 2

172/192

Les deux procdures impliques dans cette action sont les suivantes :
Private Sub lnkAjout_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkAjout.Click ' on affiche la vue [ajout] afficheAjout() End Sub Private Sub afficheAjout() ' affiche la vue ajout vueAjout.Visible = True vueFormulaire.Visible = False vueProduits.Visible = False vueErreurs.Visible = False End Sub

Rappelons que la vue [Ajout] a des composants de validation : nom


rfvNom rfvPrix cvPrix

type rle RequiredFieldValidator vrifie la prsence d'une valeur dans [txtNom] RequiredFieldValidator vrifie la prsence d'une valeur dans [txtPrix] CompareValidator vrifie que prix>=0

La procdure de gestion du clic sur le bouton [Ajouter] est la suivante :


Private Sub btnAjouter_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAjouter.Click ' ajout d'un nouvel lment la table des produits ' il faut tout d'abord que les donnes soient valides rfvNom.Validate() rfvPrix.Validate() cvPrix.Validate() If Not rfvNom.IsValid Or Not rfvPrix.IsValid Or Not cvPrix.IsValid Then ' de nouveau le formulaire de saisie afficheAjout() Exit Sub End If ' on cre une ligne Dim produit As DataRow = dtProduits.NewRow produit("nom") = txtNom.Text.Trim produit("prix") = txtPrix.Text.Trim ' on ajoute la ligne la table Application.Lock() Try dtProduits.Rows.Add(produit) lblInfo3.Text = "Ajout russi" ' nettoyage txtNom.Text = "" txtPrix.Text = "" Catch ex As Exception lblInfo3.Text = String.Format("Erreur : {0}", ex.Message) End Try Application.UnLock() End Sub

Tout d'abord, on excute les contrles de validit des trois composants [rfvNom, rfvPrix, cvPrix]. Si l'un des contrles choue, la vue [Ajout] est raffiche avec les messages d'erreurs des composants de validation. Voici un exemple : Composants serveur - 2 173/192

Si les donnes sont valides, on prpare la ligne insrer dans la table [dtProduits].
' on cre une ligne Dim produit As DataRow = dtProduits.NewRow produit("nom") = txtNom.Text.Trim produit("prix") = txtPrix.Text.Trim

On cre tout d'abord une nouvelle ligne de la table [dtProduits] avec la mthode [DataTable.NewRow]. Cette ligne aura les trois colonnes [id, nom, prix] de la table [dtProduits]. Les colonnes [nom, prix] sont renseignes avec les valeurs saisies dans le formulaire d'ajout. La colonne [id] elle n'est pas renseigne. Cette colonne est de type [AutoIncrement], c.a.d. que le SGBD donnera comme cl un nouveau produit, la cl maximale existante augmente de 1. La ligne [produit] cre ici est dtache de la table [dtProduits]. Il nous faut maintenant l'insrer dans cette table. Parce qu'on va mettre jour la table [dtProduits] on active la synchronisation inter-clients :
Application.Lock()

Ceci fait, on essaie d'ajouter la ligne la table [dtProduits]. Si on russit, on fait afficher un message de russite sinon un message d'erreur.
Try dtProduits.Rows.Add(produit) lblInfo3.Text = "Ajout russi" ' nettoyage txtNom.Text = "" txtPrix.Text = "" Catch ex As Exception lblInfo3.Text = String.Format("Erreur : {0}", ex.Message) End Try

Il n'y a normalement aucune exception possible. C'est par prcaution qu'on a mis nanmoins en place une gestion d'exception. L'ajout fait, on quitte la section critique par [Application.Unlock].

3.6.15 Modification d'un produit


Pour modifier un produit, l'utilisateur utilise le lien [Modifier] de la ligne du produit :

On passe alors en mode dition :

Grce au composant [DataGrid] ce rsultat est obtenu avec trs peu de code : Composants serveur - 2 174/192

Private Sub DataGrid1_EditCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles DataGrid1.EditCommand ' on met l'lment courant en mode dition DataGrid1.EditItemIndex = e.Item.ItemIndex ' on raffiche les produits afficheProduits(DataGrid1.CurrentPageIndex, DataGrid1.PageSize) End Sub

Le clic sur le lien [Modifier] provoque l'excution, ct serveur, de la procdure [DataGrid1_EditCommand]. Le composant [DataGrid1] a un champ [EditItemIndex]. La ligne ayant pour index la valeur de [EditItemIndex] est mise en mode dition. Chaque valeur de la ligne ainsi mise jour peut tre modifie dans une bote de saisie comme le montre la copie d'cran ci-dessus. Nous rcuprons donc l'index du produit sur lequel a eu lieu le clic sur le lien [Modifier] et l'affectons l'attribut [EditItemIndex] du composant [DataGrid1]. Il ne nous reste plus qu' rafficher la vue [produits]. Elle appratra identique ce qu'elle tait mais avec une ligne en mode dition. Le lien [Annuler] du produit en cours d'dition permet l'utilisateur d'abandonner sa mise jour. La procdure lie ce lien est la suivante :
Private Sub DataGrid1_CancelCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles DataGrid1.CancelCommand ' on raffiche les produits afficheProduits(DataGrid1.CurrentPageIndex, DataGrid1.PageSize) End Sub

Elle se contente de rafficher la vue [produits]. On pourrait tre tent de mettre -1 dans [DataGrid1.EditItemIndex] pour annuler me mode de mise jour. En fait, nous savons que la procdure [Page_Load] le fait systmatiquement. Il n'y donc pas lieu de le refaire. La modification est valide par le lien [Mettre jour] de la ligne en cours d'dition. La procdure suivante est alors excute :
Private Sub DataGrid1_UpdateCommand(ByVal source As Object, ByVal e As System.Web.UI.WebControls.DataGridCommandEventArgs) Handles DataGrid1.UpdateCommand ' cl du produit modifier Dim idProduit As Integer = CType(DataGrid1.DataKeys(e.Item.ItemIndex), Integer) ' lments modifis Dim nom As String = CType(e.Item.Cells(0).Controls(0), TextBox).Text.Trim Dim prix As String = CType(e.Item.Cells(1).Controls(0), TextBox).Text.Trim ' modifications valides ? lblInfo2.Text = "" If nom = String.Empty Then lblInfo2.Text += "[Indiquez un nom]" If prix = String.Empty Then lblInfo2.Text += "[Indiquez un prix]" Else Dim nouveauPrix As Double Try nouveauPrix = CType(prix, Double) If nouveauPrix < 0 Then Throw New Exception Catch ex As Exception lblInfo2.Text += "[prix invalide]" End Try End If ' si erreur, on raffiche la page de mise jour If lblInfo2.Text <> String.Empty Then ' on remet la ligne en mode mise jour DataGrid1.EditItemIndex = e.Item.ItemIndex ' affichage produits afficheProduits(DataGrid1.CurrentPageIndex, DataGrid1.PageSize) ' fin Exit Sub End If ' cas o pas d'erreur - on modifie la table Try modifierProduit(idProduit, e.Item) Catch ex As Exception ' pb lblInfo2.Text = ex.Message End Try ' affichage produits afficheProduits(DataGrid1.CurrentPageIndex, DataGrid1.PageSize) End Sub

Comme pour la suppression, il nous faut rcuprer la cl du produit modifier afin de retrouver sa ligne dans la table des produits [dtProduits] :
' cl du produit modifier Dim idProduit As Integer = CType(DataGrid1.DataKeys(e.Item.ItemIndex), Integer)

Composants serveur - 2

175/192

Ensuite il nous faut rcuprer les nouvelles valeurs affecter la ligne. Celles-ci sont dans le composant [DataGrid1]. L'argument [e] de la procdure est l pour nous aider. [e.Item] reprssente la ligne de [DataGrid1] source de l'vnement. C'est donc la ligne en cours de mise jour puisque le lien [Mettre jour] n'existe que sur cette ligne. Cette ligne contient des colonnes dsignes par la collection [Cells] de la ligne. Ainsi [e.Item.Cells(0)] reprsente la colonne 0 de la ligne mise jour. Nous savons que les nouvelles valeurs sont dans des botes de saisie. La collection [e.Item.Cells(i).Controls] reprsente la collection des contrles de la colonne i de la ligne [e.Item]. Les deux instructions suivantes rcuprent les valeurs des botes de saisie de la ligne mise jour dans [DataGrid1] :
' lments modifis Dim nom As String = CType(e.Item.Cells(0).Controls(0), TextBox).Text.Trim Dim prix As String = CType(e.Item.Cells(1).Controls(0), TextBox).Text.Trim

Nous avons donc les nouvelles valeurs de la ligne modifie sous la forme de chanes de caractres. Nous vrifions ensuite si ces donnes sont valides. Le nom doit tre non vide et le prix doit tre un nombre positif ou nul :
' modifications valides ? lblInfo2.Text = "" If nom = String.Empty Then lblInfo2.Text += "[Indiquez un nom]" If prix = String.Empty Then lblInfo2.Text += "[Indiquez un prix]" Else Dim nouveauPrix As Double Try nouveauPrix = CType(prix, Double) If nouveauPrix < 0 Then Throw New Exception Catch ex As Exception lblInfo2.Text += "[prix invalide]" End Try End If

En cas d'erreur, le label [lblInfo2] affichera un message d'erreur et on se contente de rafficher la mme page :
' si erreur, on raffiche la page de mise jour If lblInfo2.Text <> String.Empty Then ' on remet la ligne en mode mise jour DataGrid1.EditItemIndex = e.Item.ItemIndex ' affichage produits afficheProduits(DataGrid1.CurrentPageIndex, DataGrid1.PageSize) ' fin Exit Sub End If

Ce que ne montre pas le code ci-dessus, c'est que les valeurs saisies sont perdues. En effet, [DataGrid1] est li aux donnes de la table [dtProduits] qui contient les valeurs d'origine de la ligne modifie. Voici un exemple.

La rponse obtenue est la suivante :

Composants serveur - 2

176/192

On voit qu'on a perdu les valeurs initialement saisies. Dans une application professionnelle, ce ne serait probablement pas acceptable. On atteint ici certaines limites du mode standard de mise jour du composant [DataGrid]. Il serait prfrable d'avoir une vue [Modification] analogue la vue [Ajout]. Si les donnes sont valides, elles sont utilises pour faire la mise jour de la table [dtProduits] :
' cas o pas d'erreur - on modifie la table Try modifierProduit(idProduit, e.Item) Catch ex As Exception ' pb lblInfo2.Text = ex.Message End Try ' affichage produits afficheProduits(DataGrid1.CurrentPageIndex, DataGrid1.PageSize)

Pour la mme raison voque lors de la suppression d'un produit, la modification peut chouer. En effet, entre le moment o le client a lu la table [dtProduits] et celui o il va modifier l'un de ses produits, celui-ci a pu tre supprim par un autre client. La procdure [modifierProduit] gre ce cas en lanant une exception. Celle-ci est ici gre. Aprs russite ou chec de la mise jour, l'application renvoie la vue [produits] au client. Il nous reste voir comment la procdure [modifierProduit] ralise la mise jour :
Private Sub modifierProduit(ByVal idProduit As Integer, ByVal item As DataGridItem) Dim erreur As String Try ' synchronisation Application.Lock() ' recherche de la ligne modifier Dim ligne As DataRow = dtProduits.Rows.Find(idProduit) If ligne Is Nothing Then erreur = String.Format("Produit [{0}] inexistant", idProduit) Else ' on modifie la ligne With ligne .Item("nom") = CType(item.Cells(0).Controls(0), TextBox).Text.Trim .Item("prix") = CType(item.Cells(1).Controls(0), TextBox).Text.Trim End With End If Catch ex As Exception erreur = String.Format("Erreur lors de la modification : {0}", ex.Message) Finally ' fin de synchronisation Application.UnLock() End Try ' on lance une exception si erreur If erreur <> String.Empty Then Throw New Exception(erreur) End Sub

Nous ne rentrerons pas dans les dtails de cette procdure dont le code est analogue celui de la procdure [supprimerProduit] qui a t expliqu longuement. Voyons simplement deux exemples. Tout d'abord nous modifions le prix du produit [produit1] :

Composants serveur - 2

177/192

L'utilisation du lien [Mettre jour] ci-dessus amne la rponse suivante :

La modification est bien prsente dans les composants [DataGrid] 1 et 2 qui refltent l'tat actuel de la table [dtProduits]. Elle est galement prsente dans le composant [DataGrid4] qui est une vue sur les lignes modifies, celles-ci tant affiches avec leurs valeurs d'origine. Maintenant voyons un cas de conflit d'accs. Comme pour l'exemple de suppression, nous allons utiliser deux clients web diffrents. Un client [Mozilla] lit la table [dtProduits] et se met en modification du produit [produit1] :

Un client [Internet Explorer] s'apprte lui supprimer le produit [produit1] :

Le client [Internet Explorer] supprime [produit1] :

Composants serveur - 2

178/192

On remarquera que [produit1] n'est plus dans le tableau des lignes modifies mais dans celui des lignes supprimes. Le client [Mozilla] lui valide sa mise jour :

Le client [Mozilla] reoit la rponse suivante sa mise jour :

Composants serveur - 2

179/192

Il peut constater que [produit1] a t supprim puisqu'il est dans la liste des produits supprims.

3.7 Application web de mise jour de la table physique des produits


3.7.1 Solutions proposes
L'application prcdente tait davantage un cas d'cole destin montrer la gestion d'un objet [DataTable] en cache qu'un cas courant. Il faut bien en effet, un moment ou un autre, mettre jour la source relle des donnes. On peut opter pour deux stratgies diffrentes : 1. on utilise le cache [dtProduits] en mmoire pour mettre jour la source des donnes. On peut prvoir une page situe dans l'arborescence web de l'application prcdente afin d'avoir accs au cache [dtProduits] de celle-ci. Cette page offrirait un administrateur la possibilit de rpercuter sur la source physique des donnes les modification en cache dans [dtProduits]. On pourrait pour ce faire, ajouter une nouvelle mthode la classe d'accs [produits] qui admettrait pour paramtre le cache [dtProduits] et qui avec ce cache mettrait jour la source physique des donnes. on met jour la source de donnes physique en mme temps que le cache.

2.

La stratgie n 1 permet de n'ouvrir qu'une connexion avec la source de donnes physique. La stratgie n 2 ncessite une connexion chaque mise jour. Selon la disponibilit des connexions on pourra prfrer l'une ou l'autre des stratgies. Parce que nous avons les outils pour la mettre en oeuvre (la classe [produits]), nous choisissons la stratgie n 2.

3.7.2 Mise en oeuvre de la solution 1


Nous choisissons par souci de continuit avec l'application prcdente la statgie suivante : la source physique est mise jour en mme temps que le cache le cache n'est construit qu'une fois l'initialisation de l'application dans [global.asax.vb]. Cela signifie que si la source de donnes physique est mise jour par d'autres clients que les clients web, ceux-ci ne voient pas ces modifications. Ils ne voient que celles qu'eux-mmes apportent la table en cache. Pour faire la mise jour de la source physique des donnes, nous avons besoin d'une instance de la classe d'accs aux produits. Chaque client pourrait avoir la sienne. On peut aussi partager une unique instance qui serait cre par l'application son dmarrage. C'est la solution que nous choisissons ici. Le code de contrle [global.asax.vb] est modifi comme suit :
Imports System Imports System.Web ...

Composants serveur - 2

180/192

Public Class Global Inherits System.Web.HttpApplication Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs) ' on rcupre les informations de configuration Dim chanedeConnexion As String = ConfigurationSettings.AppSettings("OLEDBStringConnection") Dim defaultProduitsPage As String = ConfigurationSettings.AppSettings("defaultProduitsPage") Dim erreurs As New ArrayList ... ' ici pas d'erreurs de configuration ' on cre un objet produits Dim objProduits As New produits(chanedeConnexion) Dim dtProduits As DataTable Try dtProduits = objProduits.getProduits Catch ex As ExceptionProduits 'il y a eu erreur d'accs aux produits, on le note dans l'application Application("erreurs") = ex.erreurs Exit Sub Catch ex As Exception ' erreur non gre erreurs.Add(ex.Message) Application("erreurs") = erreurs ' exit sub End Try ' ici pas d'erreurs d'initialisation ... ' on mmorise l'instance d'accs aux donnes Application("objProduits") = objProduits End Sub Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs) ... End Sub End Class

Une instance de la classe d'accs aux donnes a t mmorise dans l'application, associe la cl [objProduits]. Chaque client utilisera cette instance pour accder la source physique des donnes. Elle sera rcupre dans la procdure [Page_Load] de [main.aspx.vb] :
Protected objProduits As produits .... Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ... ' on rcupre l'instance d'accs aux produits objProduits = CType(Application("objProduits"), produits) ... End Sub

L'instance d'accs aux donnes [objProduits] est accessible toutes les mthodes de la page. Elle sera utilise pour les trois oprations de mise jour : l'ajout, la suppression, la modification. La procdure d'ajout est modifie comme suit :
Private Sub btnAjouter_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnAjouter.Click ... ' on ajoute la ligne Application.Lock() Try ' on ajoute la ligne la table en cache dtProduits.Rows.Add(produit) ' on ajoute la ligne la source physique Dim nouveauProduit As sProduit With nouveauProduit .nom = CType(produit("nom"), String) .prix = CType(produit("prix"), Double) End With objProduits.ajouterProduit(nouveauProduit) ' suivi lblInfo3.Text = "Ajout russi" ' nettoyage txtNom.Text = "" txtPrix.Text = "" Catch ex As Exception ' erreur lblInfo3.Text = String.Format("Erreur : {0}", ex.Message) End Try

Composants serveur - 2

181/192

Application.UnLock() End Sub

La procdure de modification est modifie comme suit :


Private Sub modifierProduit(ByVal idProduit As Integer, ByVal item As DataGridItem) Dim erreur As String Try ' synchronisation Application.Lock() ' recherche de la ligne modifier Dim ligne As DataRow = dtProduits.Rows.Find(idProduit) If ligne Is Nothing Then erreur = String.Format("Produit [{0}] inexistant", idProduit) Else ' on modifie la ligne dans le cache With ligne .Item("nom") = CType(item.Cells(0).Controls(0), TextBox).Text.Trim .Item("prix") = CType(item.Cells(1).Controls(0), TextBox).Text.Trim End With ' on modifie la ligne dans la source physique Dim nouveauProduit As sProduit With nouveauProduit .id = idProduit .nom = CType(ligne.Item("nom"), String) .prix = CType(ligne.Item("prix"), Double) End With objProduits.modifierProduit(nouveauProduit) End If Catch ex As Exception erreur = String.Format("Erreur lors de la modification : {0}", ex.Message) Finally ' fin de synchronisation Application.UnLock() End Try ' on lance une exception si erreur If erreur <> String.Empty Then Throw New Exception(erreur) End Sub

La procdure de suppression est modifie comme suit :


Private Sub supprimerProduit(ByVal idProduit As Integer) Dim erreur As String Try ' synchronisation Application.Lock() ' recherche de la ligne supprimer Dim ligne As DataRow = dtProduits.Rows.Find(idProduit) If ligne Is Nothing Then erreur = String.Format("Produit [{0}] inexistant", idProduit) Else ' suppression de la ligne dans le cache ligne.Delete() ' suppression de la ligne dans la source physique objProduits.supprimerProduit(idProduit) End If Catch ex As Exception erreur = String.Format("Erreur de suppression : {0}", ex.Message) Finally ' fin de synchronisation Application.UnLock() End Try ' on lance une exception si erreur If erreur <> String.Empty Then Throw New Exception(erreur) End Sub

3.7.3 Tests
Nous partons de la table de donnes suivante dans un fichier ACCESS :

Composants serveur - 2

182/192

Un client web est lanc :

Nous ajoutons un produit :

Nous supprimons [produit1] :

Composants serveur - 2

183/192

Nous modifions le prix de [produit2] :

Aprs ces modifications, nous regardons le contenu de la table [liste] dans la base ACCESS :

Les trois modification ont t correctement prises en compte dans la table physique. Examinons maintenant un cas de conflit. Nous supprimons, directement sous ACCESS, la ligne de [produit2] :

Composants serveur - 2

184/192

Nous revenons notre client web. Celui-ci ne voit pas la suppression qui a t faite et veut supprimer son tour [produit2] :

Il obtient la rponse suivante :

La ligne [produit2] a bien t enleve du cache comme l'indique la liste des produits supprims. La suppression de [produit2] dans la source physique a elle chou comme l'indique le message d'erreur.

3.7.4 Mise en oeuvre de la solution 2


Dans la solution prcdente, les clients web mettent simultanment jour la source de donnes physique mais il ne voit pas les modifications apportes par les autres clients. Ils ne voient que les leurs. Nous voudrions maintenant qu'un client puisse voir la source de donnes physique telle qu'elle est actuellement et non pas telle qu'elle tait au lancement de l'application. Pour cela, on va offrir au client une nouvelle option :

Avec l'option [Rafrachir], le client force une relecture de la source de donnes physique. Pour que ceci n'affecte pas les autres clients, il faut que la table issue de cette lecture appartienne au client qui fait le rafrachissement et ne soit pas partage avec les autres clients. C'est la premire diffrence avec l'application prcdente. Le cache [dtProduits] de la source de donnes sera construit par chaque client et non pas par l'application elle-mme. La modification est faite dans [global.asax.vb] :
Imports Imports Imports Imports Imports Imports Imports Imports System System.Web System.Web.SessionState st.istia.univangers.fr System.Configuration System.Data Microsoft.VisualBasic System.Collections

Public Class Global Inherits System.Web.HttpApplication Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs) ' on rcupre les informations de configuration ... ' ici pas d'erreurs de configuration ' on cre un objet produits Dim objProduits As New produits(chanedeConnexion) ' on mmorise le nbre de produits par page Application("defaultProduitsPage") = defaultProduitsPage ' on mmorise l'instance d'accs aux donnes Application("objProduits") = objProduits

Composants serveur - 2

185/192

End Sub Sub Session_Start(ByVal sender As Object, ByVal e As EventArgs) ' init des variables de session If IsNothing(Application("erreurs")) Then ' on met la source de donnes en cache Dim dtProduits As DataTable Try Application.Lock() dtProduits = CType(Application("objProduits"), produits).getProduits Catch ex As ExceptionProduits 'il y a eu erreur d'accs aux produits, on le note dans la session Session("erreurs") = ex.erreurs Exit Sub Finally Application.UnLock() End Try ' on met le cache [dtProduits] dans la session Session("dtProduits") = dtProduits ' vue sur la table des produits Session("dvProduits") = dtProduits.DefaultView ' nbre de produits par page Session("nbProduitsPage") = Application("defaultProduitsPage") ' page courante affiche Session("pageCourante") = 0 End If End Sub End Class

Les informations mises en session sont rcupres chaque requte dans la procdure [Page_Load] :
' donnes Protected Protected Protected Protected Protected page dtProduits As DataTable dvProduits As DataView objProduits As produits nbProduitsPage As Integer pageCourante As Integer

Private Sub Page_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ' on regarde si l'application est en erreur If Not IsNothing(Application("erreurs")) Then ' l'application ne s'est pas initialise correctement afficheErreurs(CType(Application("erreurs"), ArrayList)) Exit Sub End If ' on regarde si la session est en erreur If Not IsNothing(Session("erreurs")) Then ' la session ne s'est pas initialise correctement afficheErreurs(CType(Session("erreurs"), ArrayList)) Exit Sub End If ' on rcupre une rfrence sur la table des produits dtProduits = CType(Session("dtProduits"), DataTable) ' on rcupre la vue sur les produits dvProduits = CType(Session("dvProduits"), DataView) ' on rcupre le nombre de produits par page nbProduitsPage = CType(Session("nbProduitsPage"), Integer) ' on rcupre la page courante pageCourante = CType(Session("pageCourante"), Integer) ' on rcupre l'instance d'accs aux produits objProduits = CType(Application("objProduits"), produits) ' on annule une ventuelle mise jour en cours DataGrid1.EditItemIndex = -1 '1re requte If Not IsPostBack Then ' on affiche le formulaire initial txtPages.Text = nbProduitsPage.ToString afficheFormulaire() End If End Sub

Les informations rcupres dans la session seront sauvegardes dans cette mme fin de session chaque fin de requte :
Private Sub Page_PreRender(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.PreRender ' on mmorise certaines informations dans la session Session("dtProduits") = dtProduits Session("dvProduits") = dvProduits Session("nbProduitsPage") = nbProduitsPage Session("pageCourante") = pageCourante

Composants serveur - 2

186/192

End Sub

L'vnement [PreRender] est celui qui signale que la rponse va tre envoye au client. On en profite pour sauvegarder dans la session toutes les donnes conserver. C'est excessif dans la mesure o assez souvent seules certaines d'entre-elles ont chang de valeur. Cette sauvegarde systmatique a l'avantage de nous dcharger de la gestion de session dans les autres mthodes de la page. L'opration de rafrachissement du cache est assure par la procdure suivante :
Private Sub lnkRefresh_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles lnkRefresh.Click ' on doit rafrachir le cache [dtProduits] avec la source de donnes physique ' dbut synchro Application.Lock() Try ' la table des produits dtProduits = CType(Application("objProduits"), produits).getProduits ' on mmorise le filtre actuel Dim filtre As String = dvProduits.RowFilter ' on cre la nouvelle vue filtre dvProduits = New DataView(dtProduits) ' on remet le filtre dvProduits.RowFilter = filtre Catch ex As ExceptionProduits 'il y a eu erreur d'accs aux produits, on le note dans la session Session("erreurs") = ex.erreurs ' on fait afficher la vue [erreurs] afficheErreurs(ex.erreurs) ' fini Exit Sub Finally ' fin synchro Application.UnLock() End Try ' a s'est bien pass - on affiche les produits partir de la 1re page afficheProduits(0, nbProduitsPage) End Sub

La procdure rgnre de nouvelles valeurs pour le cache [dtProduits] et la vue [dvProduits]. Celles-ci seront places dans la session par la procdure [Page_PreRender] dcrite plus haut. Une fois le cache [dtProduits] reconstruit, on affiche les produits partir de la premire page. Voici un exemple d'excution. Un client [mozilla] est lanc et affiche les produits :

Un client [Internet Explorer] fait de mme : Composants serveur - 2 187/192

Le client [Mozilla] supprime [produit1] modifie [produit2] et ajoute un nouveau produit. Il obtient la nouvelle page suivante :

Le client [Internet Explorer] veut supprimer [produit1].

Composants serveur - 2

188/192

Il obtient la rponse suivante :

On lui a signal que [produit1] n'existait plus. L'utilisateur dcide alors de rafrachir son cache avec le lien [Rafrachir] ci-dessus. Il obtient la rponse suivante :

Composants serveur - 2

189/192

Il a maintenant la mme source de donnes que le client [Mozilla].

3.8 Conclusion
Dans ce chapitre, nous avons pass un long moment sur les conteneurs de donnes et leur liaisons avec des sources de donnes. Nous avons termin en montrant comment on pouvait mettre jour une source de donnes l'aide d'un composant [DataGrid]. Nous avons pris comme source de donnes une table de base de donnes. Mme si le composant [DataGrid] facilite un peu les choses pour la reprentation des donnes, la vritable difficult ne rside pas dans la partie prsentation mais bien dans la gestion des mises jour de la source de donnes faites par les diffrents clients. Des conflits d'accs peuvent surgir et doivent tre grs. Ici, nous les avons grs dans le contrleur par l'utilisation de [Application.Lock]. Il serait probablement plus avis de synchroniser les accs la source de donnes dans la classe d'accs celles-ci afin que le contrleur n'ait pas se proccuper de tels dtails qui ne sont pas de son ressort. Dans la pratique, les tables d'une base de donnes sont lies entre-elles par des relations et leur mise jour doit tenir compte de celles-ci. Cela a des consquences tout d'abord sur la classe d'accs aux donnes qui devient plus complexe que celle ncessaire pour une table indpendante. Cela a en gnral galement des consquences sur la partie prsentation de l'application web car il faut souvent afficher non pas une unique table mais les tables lies entre-elles par des relations. Ce chapitre a permis, par ailleurs, de prsenter diffrents structures de donnes telles que [DataTable, DataView].

Composants serveur - 2

190/192

COMPOSANTS SERVEUR ASP - 1

3 3 4 5 5 6 6 10 10 10 10 11 12 13 15 16 16 18 23 28 29 32 34 36 39 40 41 43 43 48 56 61 61 61 61 61 65 67 71 72 77 79 79 80 81 82 84 85 85 85 89 96 97 98 101 102 107 107 191/192

1.1 INTRODUCTION 1.2 LE CONTEXTE D'EXECUTION DES EXEMPLES 1.3 LE COMPOSANT LABEL 1.3.1 UTILISATION 1.3.2 LES TESTS 1.3.3 CONSTRUIRE L'APPLICATION AVEC WEBMATRIX 1.4 LE COMPOSANT LITERAL 1.4.1 UTILISATION 1.5 LE COMPOSANT BUTTON 1.5.1 UTILISATION 1.5.2 TESTS 1.5.3 LES REQUETES DU CLIENT 1.5.4 GERER L'EVENEMENT CLICK D'UN OBJET BUTTON 1.5.5 LES EVENEMENTS DE LA VIE D'UNE APPLICATION ASP.NET 1.6 LE COMPOSANT TEXTBOX 1.6.1 UTILISATION 1.6.2 TESTS 1.6.3 LE ROLE DU CHAMP __VIEWSTATE 1.6.4 AUTRES PROPRIETES DU COMPOSANT TEXTBOX 1.7 LE COMPOSANT DROPDOWNLIST 1.8 LE COMPOSANT LISTBOX 1.9 LES COMPOSANTS CHECKBOX, RADIOBUTTON 1.10 LES COMPOSANTS CHECKBOXLIST, RADIOBUTTONLIST 1.11 LES COMPOSANTS PANEL,LINKBUTTON 1.12 POUR POURSUIVRE... 1.13 COMPOSANTS SERVEUR ET CONTROLEUR D'APPLICATION 1.14 EXEMPLES D'APPLICATIONS MVC AVEC COMPOSANTS SERVEUR ASP 1.14.1 EXEMPLE 1 1.14.2 EXEMPLE 2 1.14.3 EXEMPLE 3 2 COMPOSANTS SERVEUR ASP - 2

2.1 INTRODUCTION 2.2 LES COMPOSANTS DE VALIDATION DE DONNEES 2.2.1 INTRODUCTION 2.2.2 REQUIREDFIELDVALIDATOR 2.2.3 COMPAREVALIDATOR 2.2.4 CUSTOMVALIDATOR, RANGEVALIDATOR 2.2.5 REGULAREXPRESSIONVALIDATOR 2.2.6 VALIDATIONSUMMARY 2.3 COMPOSANTS LISTCONTROL ET LIAISON DE DONNEES 2.3.1 CODE DE PRESENTATION DES COMPOSANTS 2.3.2 LIAISON A UNE SOURCE DE DONNEES DE TYPE ARRAY 2.3.3 LIAISON A UNE SOURCE DE DONNEES DE TYPE ARRAYLIST 2.3.4 SOURCE DE DONNEES DE TYPE DATATABLE 2.3.5 SOURCE DE DONNEES DE TYPE DATASET 2.3.6 SOURCE DE DONNEES DE TYPE HASHTABLE 2.3.7 LES DIRECTIVES D'IMPORTATION D'ESPACES DE NOMS 2.4 COMPOSANT DATAGRID ET LIAISON DE DONNEES 2.4.1 AFFICHAGE D'UNE SOURCE DE DONNEES ARRAY, ARRAYLIST, DATATABLE, DATASET 2.5 VIEWSTATE DES COMPOSANTS LISTES DE DONNEES 2.6 AFFICHAGE D'UNE LISTE DE DONNEES A L'AIDE D'UN DATAGRID PAGINE ET TRIE 2.6.1 LES CLASSES METIER 2.6.2 LES VUES 2.6.3 CONFIGURATION DU DATAGRID 2.6.4 LES CONTROLEURS 2.7 COMPOSANT DATALIST ET LIAISON DE DONNEES 2.7.1 APPLICATION Composants serveur - 2

2.7.2 LES CLASSES METIER 2.7.3 LES VUES 2.7.4 CONFIGURATION DES COMPOSANTS [DATALIST] 2.7.5 LES CONTROLEURS 2.8 COMPOSANT REPEATER ET LIAISON DE DONNEES 2.9 APPLICATION 2.9.1 LA STRUCTURE MVC DE L'APPLICATION 2.9.2 LES VUES DE L'APPLICATION 2.9.3 LE CODE DE CONTROLE DE L'APPLICATION 2.9.4 TESTS 2.9.5 CONCLUSION 3 COMPOSANTS SERVEUR ASP - 3

107 107 109 111 113 114 114 115 121 125 126 127 127 127 127 127 129 130 131 131 132 134 134 135 139 139 140 140 144 144 144 145 145 148 150 150 151 154 156 158 160 162 162 163 164 165 168 169 172 174 180 180 180 182 185 190

3.1 INTRODUCTION 3.2 GERER LES EVENEMENTS ASSOCIES AUX DONNEES DES COMPOSANTS A LIAISON DE DONNEES 3.2.1 L'EXEMPLE 3.2.2 LA CONFIGURATION DES COMPOSANTS 3.2.3 LE CODE DE PRESENTATION DE LA PAGE 3.2.4 LE CODE DE CONTROLE DE LA PAGE 3.3 APPLICATION - GESTION D'UNE LISTE D'ABONNEMENTS 3.3.1 INTRODUCTION 3.3.2 FONCTIONNEMENT 3.3.3 CONFIGURATION DES CONTENEURS DE DONNEES 3.3.4 LA PAGE DE PRESENTATION 3.3.5 LES CONTROLEURS 3.4 GERER UN [DATALIST] PAGINE 3.4.1 FONCTIONNEMENT 3.4.2 CODE DE PRESENTATION 3.4.3 CODE DE CONTROLE 3.4.4 CONCLUSION 3.5 CLASSE D'ACCES A UNE BASE DE PRODUITS 3.5.1 LA CLASSE EXCEPTIONPRODUITS 3.5.2 LA STRUCTURE [SPRODUIT] 3.5.3 LA CLASSE PRODUITS 3.5.4 TESTS DE LA CLASSE [PRODUITS] 3.6 APPLICATION WEB DE MISE A JOUR DE LA TABLE DES PRODUITS EN CACHE 3.6.1 INTRODUCTION 3.6.2 FONCTIONNEMENT ET VUES 3.6.3 CONFIGURATION DES CONTENEURS DE DONNEES 3.6.4 LE CODE DE PRESENTATION DE L'APPLICATION 3.6.5 LE CODE DE CONTROLE [GLOBAL.ASAX] 3.6.6 LE CODE DE CONTROLE [MAIN.ASPX.VB] 3.6.7 LES DONNEES D'INSTANCE 3.6.8 LA PROCEDURE [PAGE_LOAD] DE CHARGEMENT DE LA PAGE 3.6.9 AFFICHAGE DES VUES [ERREURS], [FORMULAIRE] ET [AJOUT] 3.6.10 VALIDATION DE LA VUE [FORMULAIRE] 3.6.11 AFFICHAGE DE LA VUES [PRODUITS] 3.6.12 PAGINATION ET TRI DE [DATAGRID1] 3.6.13 SUPPRESSION D'UN PRODUIT 3.6.14 AJOUT D'UN PRODUIT 3.6.15 MODIFICATION D'UN PRODUIT 3.7 APPLICATION WEB DE MISE A JOUR DE LA TABLE PHYSIQUE DES PRODUITS 3.7.1 SOLUTIONS PROPOSEES 3.7.2 MISE EN OEUVRE DE LA SOLUTION 1 3.7.3 TESTS 3.7.4 MISE EN OEUVRE DE LA SOLUTION 2 3.8 CONCLUSION

Composants serveur - 2

192/192

S-ar putea să vă placă și