Delphi ASP.NET Tutorial - John COLIBRI. |
- résumé : la première application Asp.Net, l'architecture Asp.Net de base, les traitements usuels, les bases de données avec les DataGrids
- mots clé : Asp.Net - CodeBehind, PostBack, - fichiers Aspx, Global.Asax -
WebForm, DataGrid, ListBox, Ado.Net, Borland Data Provider, Interbase
- logiciel utilisé : Windows XP personnel, Delphi 2006, Asp.Net 1.1, Cassini Web Server
- matériel utilisé : Pentium 2.800Mhz, 512 Meg de mémoire, 250 G disque
- champ d'application : Delphi 8, Delphi 2005, Delphi 2006, sur Windows .Net Framework - Mono
- niveau : développeur Asp.Net - développeur Delphi
- plan:
1 - Introduction Ce tutorial va présenter les principales technique pour construire des
application Internet utilisant Asp.Net: - nous commencerons par détailler une première application simple.
- après la première application simple, nous décrirons l'architecture ASP.NET
- puis nous présenterons quelques techniques couramment utilisées: afficher sur la page .HTML, formater ces données, effectuer des transferts de page, valider la saisie
- nous terminerons par la lecture et la modifier des données provenant d'une
base de données, en particulier avec une DataGrid
Ce tutoriel présente les manipulations en détail, et les codes sources sont téléchargeables (télécharger les sources).
Si après ce premier contact vous souhaitez approfondir vos connaissances, nous vous proposons: Nous nous adressons pour ce tutorial à un programmeur Delphi:
- ayant une idée élémentaire de Delphi: Palette, Inspecteur, OnClick. Tout le reste sera expliqué
- n'ayant pas nécessairement de connaissance de programmation Asp, Asp.Net ou Internet
2 - La première application Asp.Net 2.1 - Installation préalable Nous supposons que vous avez installé Delphi (Delphi 8, Delphi 2005 ou Delphi 2006).
Si vous utilisez Delphi 8 ou Delphi 2005, il faut aussi installer le serveur de développement Cassini. Si Cassini n'est pas installé, voyez l'article
Installation Cassin qui indique comment procéder, et comment vérifier que l'installation est correcte. Pour Delphi 2006, le serveur est déjà installé par Delphi.
2.2 - La Première application Notre première application va - afficher une page comportant un petit mot de bienvenu, une boîte de saisie du nom et un bouton pour envoyer son nom au Serveur
- l'utilisateur est censé taper son nom et cliquer le bouton
- le Serveur renverra alors un remerciement
Voici comment créer cette application:
| choisissez un répertoire où seront placées les pages ASP. Nous avons choisi C:\programs\fr\asp_web\asp_net\tutorial\p_11_first_asp_net |
| lancez Delphi | | sélectionnez "fichier | new | asp.net Web application"
| |
Delphi présente un dialogue nous demandant où se trouve notre application et quel serveur web nous souhaitons utiliser. | |
Nous plaçons notre chemin et sélectionons CASSINI | | voici nos paramètres | | confirmez | | Delphi présente la forme ASP vierge
| | compilez et exécutez | |
Delphi compile, et lance automatiquement le serveur Web local Cassini:
et Cassini appelle Internet Explorer en fournissant l'adresse de notre page: |
| fermez Internet Explorer | |
Cassini se ferme, et Delphi reprend la main en mode conception |
Maintenant que nous avons vérifié que l'installation est correcte, nous remplissons la page avec quelques éléments.
Pour écrire du texte libre sur la page .HTML, nous utilisons la propriété Response de la Forme, et sa méthode Write: Nous écrivons notre texte dans le premier événement disponible, qui est
Page_Load. Cet événement, un peu similaire à un OnActivate de Win32, est pré-créé par Delphi. Il suffit donc de placer nos écritures dans cet événement. Et pour distinguer la première requête de la réponse au click du
bouton, nous utilisons la fonction de test IsPostBack. Par conséquent: | sélectionnez l'onglet .PAS de l'éditeur
| | dans l'événement Page_Load (déjà créé par Delphi), ajoutez le code qui
écrit soit le message de bienvenu, soit le message d'adieu:
procedure t_11_first_asp_net.Page_Load(sender: System.Object;
e: System.EventArgs); begin
if IsPostBack
then Response.Write('ok ')
else Response.Write('Hello <BR>');
end; // Page_Load | |
A présent, posons la boîte où l'utilisateur tapera son nom:
Puis un bouton qui permettra à l'utilisateur d'envoyer son nom vers le Serveur: |
de ce même onglet, sélectionnez un Button et déposez-le sur la Forme Renommez le bouton submit_button. Puis créez son événement Click et écrivez
au-revoir, par exemple:
procedure t_11_first_asp_net.submit_button_Click(sender: System.Object;
e: System.EventArgs); begin
Response.Write('welcome '+ name_textbox.Text+ '<BR>');
name_textbox.Visible:= False;
submit_button.Text:= 'Bye';
end; // submit_button_Click | | |
Compilez et exécutez | | Delphi compile, puis lance Cassini, qui lance Internet Explorer
| | tapez un nom, par exemple Sam et clickez "submit" |
| Internet Explorer envoie "Sam" à Cassini, qui retourne la réponse suivante:
|
3 - Fonctionnement Asp.Net 3.1 - Fonctionnement Asp.Net Représentons d'abord graphiquement les transferts de données entre le Client
et le Serveur: - au départ, le Serveur contient les fichiers .ASPX et .PAS (renommés e.aspx et e.pas pour simplifier la figure):
- l'utilisateur charge Internet Explorer et demande notre page
- Cassini construit la réponse et l'envoie à Internet Explorer
- l'utilisateur tape "Sam" et clique "send"
- Cassini reçoit cette soumission, construit puis renvoie sa réponse:
3.2 - Les données échangées
Voici, capturé à l'aide de l'utilitaire Cassini Spy les paquets Tcp/Ip échangés entre Internet Explorer et Cassini:
| l'utilisateur envoie sa requête:
-> | GET /p_11_first_asp_net/a_11_first_asp_net.aspx HTTP/1.1 -> | Accept: */* -> | Accept-Language: fr
-> | Accept-Encoding: gzip, deflate -> | User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322)
-> | Host: localhost:81 -> | Connection: Keep-Alive -> | | |
| Cassini renvoie la page:
<- | HTTP/1.1 200 OK <- | Server: Cassini/1.0.0.0 <- | Date: Tue, 21 Mar 2006 04:43:08 GMT
<- | X-AspNet-Version: 1.1.4322 <- | Set-Cookie: ASP.NET_SessionId=spedsu55n5w2xyekicwqbs3d; path=/ <- | Cache-Control: private
<- | Content-Type: text/html; charset=utf-8 <- | Content-Length: 564 <- | Connection: Close
<- | <- | Hello <BR>
<- | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<- | <- | <html> <- | <head>
<- | <title></title> <- | </head>
<- | <- | <body>
<- | <form name="_ctl0" method="post" action="a_11_first_asp_net.aspx" id="_ctl0">
<- | <input type="hidden" name="__VIEWSTATE" value="dDwyMDE4OTI5MDg4Ozs+u3wNIkzaGHMAC1thTPRREuxDXd8=" />
<- |
<- | <input name="name_textbox" type="text" id="name_textbox" style="width:137px;" />
<- | <input type="submit" name="submit_button" value="send" id="submit_button" style="width:103px;" />
<- | </form> <- | </body>
<- | </html> <- | | |
| l'utilisateur tape "Sam" et clique "send":
-> | POST /p_11_first_asp_net/a_11_first_asp_net.aspx HTTP/1.1 -> | Accept: image/gif, image/x-xbitmap, image/jpeg, image/pjpeg, application/x-shockwave-flash, */*
-> | Referer: http://localhost:81/p_11_first_asp_net/a_11_first_asp_net.aspx -> | Accept-Language: fr
-> | Content-Type: application/x-www-form-urlencoded -> | Accept-Encoding: gzip, deflate
-> | User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 1.1.4322) -> | Host: localhost:81
-> | Content-Length: 109 -> | Connection: Keep-Alive -> | Cache-Control: no-cache
-> | Cookie: ASP.NET_SessionId=spedsu55n5w2xyekicwqbs3d -> |
-> | __VIEWSTATE=dDwyMDE4OTI5MDg4Ozs%2Bu3wNIkzaGHMAC1thTPRREuxDXd8%3D&name_textbox=sam&submit_button=send | |
| Cassini renvoie la réponse:
<- | HTTP/1.1 200 OK <- | Server: Cassini/1.0.0.0 <- | Date: Tue, 21 Mar 2006 04:43:27 GMT
<- | X-AspNet-Version: 1.1.4322 <- | Cache-Control: private <- | Content-Type: text/html; charset=utf-8
<- | Content-Length: 626 <- | Connection: Close <- |
<- | ok welcome sam<BR>
<- | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<- | <- | <html> <- | <head>
<- | <title></title> <- | </head>
<- | <- | <body>
<- | <form name="_ctl0" method="post" action="a_11_first_asp_net.aspx" id="_ctl0">
<- | <input type="hidden" name="__VIEWSTATE" value="dDwyMDE4OTI5MDg4O3Q8O2w8aTwxP
js+O2w8dDw7bDxpPDE+O2k8Mz47PjtsPHQ8cDxwPG w8VGV4dDtWaXNpYmxlOz47bDxzYW07bzxmPjs+Pjs+O zs+O3Q8cDxwPGw8VGV4dDs+O2w8QnllOz4+Oz47Oz 47Pj47Pj47PiZZfJnzAsTbvXefogRPoxfeaeXi" />
<- | <input type="submit" name="submit_button" value="Bye" id="submit_button" style="width:103px;" />
<- | </form> <- | </body>
<- | </html> <- | | | Quelques remarques:
- les données échangées sont très similaires à celles qui l'auraient été si nous avions utilisé CGI ou ISAPI:
- les données fournies par l'utilisateur sont dans une balise <FORM>...</FORM>
- la soumission est envoyée par POST
- le champ caché __VIEWSTATE est le seul élément nouveau. Il contient en fait l'état de la communication, et permet essentiellement d'éviter au Serveur
de recharger à chaque soumission les données d'une base de données (si la page avait chargées des données depuis une base, ce qui n'est pas le cas ici).
Nous avons fourni ailleurs un Analyseur de ViewState, et, voici à titre d'exemple le contenu du ViewState
retournée par la dernière réponse de Cassini: - le ViewState de départ:
dDwtMjA0ODIyNzQyMjt0PDtsPGk8MT47PjtsPHQ8O2w8a TwxPjtpPDM+Oz47bDx0PHA8cDxsPFRleHQ7VmlzaWJsZT s+O2w8U2FtO288Zj47Pj47Pjs7Pjt0PHA8cDxsPFRleHQ 7PjtsPEJ5ZTs+Pjs+Ozs+Oz4+Oz4+Oz56mm3pdEpYbpOt OuxkEew1uLqgpQ== |
- voici le contenu décodé:
t<-2048227422;t<;l<i<1>;>;l<t<;l<i<1>;i<3>;>;
l<t<p<p<l<Text;Visible;>;l<Sam;o<f>;>>;>;;>; t<p<p<l<Text;>;l<Bye;>>;>;;>;>>;>>;> zšmétJXnô:ìdì5¸º ¥| |
- et voici son contenu après analyse:
tree_hash -2048227422 control 1
[1] Text=Sam, Visible=False, [3] Text=Bye, | Dans notre cas, le ViewState n'apporte rien, mais pour des pages contenant
des informations provenant de bases de données, il encode les informations qui ne sont pas déjà stockées dans les champs .HTML standards
3.3 - Fonctionnement Asp.Net
Si les données échangées sont traditionnelles, la génération des pages envoyées au Client en revanche est remarquable. Pour bien distinguer les liens entre les différents éléments, nous avons renommé les fichiers:
- le nom du répertoire, qui est automatiquement celui du projet Delphi est p_11_first_asp_net
- dans le gestionnaire de projet, nous avons renommé WebForm1.aspx, l'unité contenant le code, a_11_first_asp_net.aspx:
- dans l'analyseur de structure (en haut à gauche), nous avons renommé la CLASS tWebForm1
Dans ces conditions, le répertoire de notre projet contient les fichiers suivants:
Et: - p_11_first_asp_net.dpr ( *.bdsproj, *.cfg, *.identcache, *.rps) est le projet principal
- Global.Asax et Global.Pas correspondent à des traitements que nous pouvons éventuellement surcharger pour intervenir avant les traitements de notre page. Nous ne présenterons pas ces événements dans ce tutoriel
- Web.Config est un fichier de paramétrage de l'application
- les .DCUIL sont les fichiers de compilation intermédiaires
- le code .PAS contient le traitement de nos événements:
unit a_11_first_asp_net; interface
uses
System.Collections, System.ComponentModel,
System.Data, System.Drawing, System.Web, System.Web.SessionState,
System.Web.UI, System.Web.UI.WebControls, System.Web.UI.HtmlControls;
type
t_11_first_asp_net = class(System.Web.UI.Page)
strict private
procedure InitializeComponent;
procedure submit_button_Click(sender: System.Object; e: System.EventArgs);
strict private
procedure Page_Load(sender: System.Object; e: System.EventArgs);
strict protected
name_textbox: System.Web.UI.WebControls.TextBox;
submit_button: System.Web.UI.WebControls.Button;
procedure OnInit(e: EventArgs); override;
private public
end; // t_11_first_asp_net implementation
procedure t_11_first_asp_net.InitializeComponent;
begin
Include(Self.submit_button.Click, Self.submit_button_Click);
Include(Self.Load, Self.Page_Load);
end; // InitializeComponent
procedure t_11_first_asp_net.OnInit(e: EventArgs);
begin InitializeComponent;
inherited OnInit(e);
end; // OnInit
procedure t_11_first_asp_net.Page_Load(sender: System.Object;
e: System.EventArgs); begin
if IsPostBack
then Response.Write('ok ')
else Response.Write('Hello <BR>');
end; // Page_Load
procedure t_11_first_asp_net.submit_button_Click(sender: System.Object;
e: System.EventArgs); begin
Response.Write('welcome '+ name_textbox.Text+ '<BR>');
name_textbox.Visible:= False;
submit_button.Text:= 'Bye';
end; // submit_button_Click end. |
- et le contenu du fichier .ASPX est le suivant:
<%@ Page language="c#" Debug="true" Codebehind="a_11_first_asp_net.pas"
AutoEventWireup="false" Inherits="a_11_first_asp_net.t_11_first_asp_net" %>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head>
<title></title> </head> <body>
<form runat="server">
<ASP:TextBox id="name_textbox" runat="server" width="137px">
</ASP:TextBox>
<ASP:Button id="submit_button" runat="server" width="63px" text="send">
</ASP:Button> </form> </body>
</html> |
La partie la plus intéressante est celle contenue dans le fichier .ASPX:
- il est très similaire au fichier .HTML envoyé au Client, mais avec des annotations utilisées pour la génération de ce fichier .HTML
- la première ligne < %@ Page est une "directive asp.net" qui indique,
entre autre
- que les événements seront traités par notre fichier Pas
Codebehind="a_11_first_asp_net.pas" - que la CLASSe qui effectuera le traitement va hériter de:
Inherits="a_11_first_asp_net.t_11_first_asp_net" La CLASS que nous utilisons est déclarée par:
type t_11_first_asp_net = class(System.Web.UI.Page)
| Donc: - notre CLASS hérite de tPage (l'équivalent de tForm en Win32)
- Asp.Net va utiliser notre classe comme ancêtre pour construire la CLASS réelle qui va générer la page
- dans le reste du fichier .ASPX
- les parties runat="server" indiquent que les événements seront traités au niveau du Serveur Cassini (pas par Client Internet Explorer)
- les balises < ASP: contiennent les contrôles de la page
Pour résumer: - le fichier .ASPX contient une sorte de "modèle" de la page. C'est une sorte
de .DFM contenant l'ébauche de ce qui sera généré
- le fichier .PAS contient le code, qui sera utilisé
- pour générer du texte .HTML (Response.Write ...)
- pour traiter les événement. Lorsque l'utilisateur clique sur un Button, l'information sera retournée vers Cassini, l'événement OnClick exécuté, et ce traitement participera au contenu de la nouvelle page .HTML
Si Asp.Net créé une nouvelle CLASSe à partir ne notre CLASSe t_11_first_asp_net, où est-elle ? Nous ne la verrons jamais. Asp.Net créé cette nouvelle classe, la compile sur disque et exécute son code. Ce code est
placé dans le répertoire BIN sous le nom de p_11_first_asp_net.DLL
Voici à présent le film des événements: - nous créons notre application (le fichier .PAS et le fichier .ASPX)
- l'utilisateur demande notre page depuis Internet Explorer
- Cassini va
- charger le .PAS et le .ASPX
- créer la nouvelle CLASSe (nommée t_xxx), que nous ne verrons pas en source
- compiler le tout dans une Assembly .Net (nommée ici e.dll), qui va être
stockée dans le répertoire BIN sous le nom de p_11_first_asp_net.DLL
- une instance de la classe (c_xxx) va être créée, qui comportera l'arbre des contrôles de notre page (en programmation objet, il y a le type, le code et les données)
- la page .HTML sera construite par le code à partir de ces données mémoire, et l'ensemble va être envoyé à Internet Explorer
Une fois les données expédiées, les données mémoire sont libérées - si l'utilisateur clique sur le Bouton, une seconde requête va être envoyée à Cassini:
- Cassini va:
- recharger l'Assembly (e.dll) en économiser ainsi une compilation
- créer l'instance de la CLASSe et reconstruire en mémoire l'arbre des contrôles de notre page
- mettre les données des contrôle à jour avec les valeurs provenant de l'utilisateur ("Sam" dans notre cas). Cet arbre correspond maintenant exactement aux données telles que les voit le Client
- à l'aide de ces données actualisées, les codes correspondant à l'événement Client (le click de "send") est exécuté
- le contenu de la page est envoyé au Client
Une fois les données expédiées, les données mémoire sont libérées - le cycle continue ensuite: soumission du Client, réponse de Cassini
Pour résumer le fonctionnement - nous plaçons des contrôles dont les paramètres sont mémorisés dans .ASPX. Nous pourrions ajouter des fioritures graphiques ou autres artifices de
présentations à ce fichier: le tout sera intégré à la page que sera générée par la suite
- nous écrivons dans le fichier .PAS
- les traitements à effectuer à la création dans t_page.OnInit
- les traitements à effectuer lorsque tous les contrôles sont chargés et mis à jour dans t_page.Page_Load. La fonction IsPostback nous permet de distinguer la première génération des réponses aux "submit"
- les traitements à effectuer en réaction aux actions de l'utilisateur dans les événements des contrôles. Notez bien que le "clic" est effectué dans Internet Explorer, mais les traitements correspondants sont effectués
sur le Serveur, qui renverra la réponse à Internet Explorer
Asp.Net permet donc une séparation très agréable entre: - la partie présentation dans Asp.Net. C'est le domaine des graphistes et
autres stylistes. L'entrée des artistes
- le code de traitement dans le fichier .PAS, et en mode objet (classes, événements, héritage etc). Ici, c'est le royaume du développeur.
Voyons à présente quelques autres exemples de traitement Asp.Net.
4 - Quelques Traitements courants 4.1 - Ecrire des données
Pour ajouter du texte à la page générée, nous utilisons Response.Write. Ces instructions peuvent être placées dans n'importe quel événement appelé après que l'arbre des contrôle ait été construit et initialisé (après Page_Load)
4.2 - Transfert de Page Dans notre premier exemple, dans la réponse au "clic" nous avons rendu le TextBox invisible pour mieux présenter la réponse. Une meilleure solution consiste à envoyer une autre page pour la réponse.
Plusieurs techniques existent pour renvoyer à l'utilisateur une page différente de la page dans laquelle il vient de cliquer: la redirection et le transfert.
Pour la première technique:
- dans la construction de la réponse, la première page appelle Response.Redirect, en ajoutant à l'URL destination les paramètres en suivant une syntaxe style CGI:
Response.Redirect(url?clé_1=valeur_1&clé_2=valeur_2&clé_3=valeur_3 ...)
| - la page destinataire lit les paramètres en utilisant la propriété Request de la page et sa liste QueryString, avec la syntaxe suivante:
valeur_1:= Request.QueryString[clé_1];
valeur_2:= Request.QueryString[clé_2];
valeur_3:= Request.QueryString[clé_3]; |
Voici l'exemple: | lancez Delphi, sélectionnez "fichier | new | asp.net Web application" et tapez le nom et le répertoire de la nouvelle application
C:\programs\fr\asp_web\asp_net\tutorial\p_21_redirect_asp_net | | Delphi présente la forme ASP vierge |
| compilez et exécutez | |
ajoutez une Textbox, nommez-la name_textbox, et un Button, appelé submit_button | |
dans Page_Load redirigez la réponse vers une seconde page:
procedure T_21_redirect_asp_net.Page_Load(sender: System.Object;
e: System.EventArgs); begin
if IsPostBack
then Response.Redirect('a_21_bye.aspx?customer='+ name_textbox.Text);
end; // Page_Load | | |
créez une seconde page, en sélectionnant "File | New | Other", puis | |
Delphi crée un nouvel ensemble .ASPX / .PAS | | posez un Label sur cette nouvelle Forme
Dans l'événement Page_Load de cette page, affichez le message d'adieu:
procedure T_21_bye_form.Page_Load(sender: System.Object;
e: System.EventArgs); begin
Label1.Text:= 'Bye '+ Request.QueryString['customer'];
end; // Page_Load | | |
compilez et exécutez. Tapez "sam" dans la TextBox, et cliquez "send" | | voici la page d'adieu:
| Notez que: - les paramètres apparaissent dans la requête (la combo box Adresse ci-dessus), ce qui peut être une limitation.
- d'autre part, la technique qui consiste à envoyer un client un ordre de redirection est fort ancienne et est inclue dans .HTTP. Elle a l'inconvénient de renvoyer au Client la demande de redirection, et c'est le
Client qui va demander la nouvelle page. Il paraît plus simple de demander au Serveur de directement renvoyer la nouvelle page. C'est ce que la technique qui suit va permettre.
La méthode Transfert fonctionne ainsi: - la première page dépose les paramètres dans la liste Context.Items:
Context.Items.Add(clé_1, valeur_1);
Context.Items.Add(clé_2, valeur_2);
Context.Items.Add(clé_3, valeur_3);
Server.Transfer(url); | - la seconde page lit les paramètres de Context.Items
valeur_1:= Context.Items[clé_1];
valeur_2:= Context.Items[clé_2];
valeur_3:= Context.Items[clé_3]; | Par conséquent:
| dans a_21_redirect_asp_net.pas, mettez entre commentaire la ligne Redirect et remplacez-la par celle avec Transfer:
procedure T_21_redirect_asp_net.Page_Load(sender: System.Object;
e: System.EventArgs); begin
if IsPostBack then begin
// Response.Redirect('a_21_bye.aspx?customer='
// + name_textbox.Text;
Context.Items.Add('customer', name_textbox.Text);
Server.Transfer('a_21_bye.aspx'); end;
end; // Page_Load | | |
dans a_21_bye.pas, remplacez Request.QueryString par Context.Items :
procedure T_21_bye_form.Page_Load(sender: System.Object;
e: System.EventArgs); begin
// Label1.Text:= 'Bye '+ Request.QueryString['customer'];
Label1.Text:= System.String.Format('bye {0}',
[Context.Items['customer']]);
end; // Page_Load | | |
compilez et exécutez. Tapez "Sam" et cliquez "send" | | voici la page retournée: |
Notez aussi que: - outre Response, la page ancêtre de notre classe, tPage, possède une propriété Request qui nous permet de récupérer la plupart des paramètres
concernant la requête envoyée par l'utilisateur
- la propriété Context est extrêmement importante, car elle constitue la "colonne vertébrale" du traitement de la requête: toutes les informations
provenant de l'utilisateur et celles retournant vers lui sont directement ou indirectement liées à Context. Dans notre exemple, Context nous a permis de communiquer entre deux pages
4.3 - Validation de saisie Un autre élément très pratique est la présence de contrôles de validation de saisie. Pour vérifier que notre utilisateur tape effectivement un nom, nous utilisons un contrôle de validation:
- nous plaçons le contrôle de validation sur la Forme
- nous associons ce contrôle à une élément à vérifier (un champ vide, une valeur à tester ...) et fournissons un message d'erreur
- lorsque l'erreur est détectée, le message est affiché
Testons, par exemple, que l'utilisateur a bien rempli son nom lorsqu'il soumet sa FORMe: |
sélectionnez l'onglet "design" de la page a_21_redirect_asp_net | | dans l'onglet des contrôles Web de la palette, sélectionnez RequiredFieldValidator:
Et - donnez à sa propriété ControlToValidate la valeur name_textbox
- dans sa propriété ErrorMessage, placez un texte d'erreur. Par exemple
Le nom est vide | |
compilez et exécutez. Puis cliquez "send" SANS remplir la TextBox | | Cassini retourne une page où le message d'erreur est affiché:
|
5 - Asp.Net et Bases de Données 5.1 - Une ListBox affichant un tableau
Commençons par afficher dans une ListBox les données provenant d'un tableau: - nous remplissons un tableau avec des chaînes
- nous relions ListBox.DataSource au tableau
- nous appelons DataBind pour remplir la Listbox.
Voici le premier exemple: |
créez une nouvelle application Asp.Net, p_listbox_array_binding_2 et compilez | | posez une tListBox sur la Forme |
| remplissez un tableau avec des valeur: - dans la zone PUBLIC de t_31_listbox_array_binding_form déclarez un tableau:
m_array: array[1..5] of System.String; |
- dans t_31_listbox_array_binding_form.OnInit, remplissez le tableau et reliez ListBox1.DataSource à ce tableau :
procedure t_31_listbox_array_binding_form.OnInit(e: EventArgs);
var l_index: Integer; begin
InitializeComponent; inherited OnInit(e);
m_array[1]:= 'asp_net'; m_array[2]:= 'ado_net';
m_array[3]:= 'delphi_2006'; m_array[4]:= 'win32_to_.net';
m_array[5]:= 'tcp_ip_sockets';
ListBox1.DataSource:= m_array; end; |
| | posez un tButton sur la Forme, nommez-le "bind_button", créez son événement OnClick et appelez tPage.DataBind:
procedure t_31_listbox_array_binding_form.bind_button_Click(sender: System.Object;
e: System.EventArgs); begin
DataBind; end; // bind_button_Click | |
| posez un tButton sur la Forme, appelez le "submit_button" | |
pour visualiser l'élément de la ListBox sélectionnez, - posez un tLabel sur la Forme
- créez l'événement ListBox1.SelectedIndexChanged et affichez l'index de l'item sélectionné:
procedure t_31_listbox_array_binding_form.ListBox1_SelectedIndexChanged(sender: System.Object;
e: System.EventArgs); begin
Label1.Text:= 'selected index= '+ ListBox1.SelectedIndex.ToString;
end; // ListBox1_SelectedIndexChanged | |
| compilez et exécutez | | Cassini envoie une page avec une ListBox vide:
| | cliquez "do_bind" |
| Cassini remplit la ListBox et renvoie la page: |
| sélectionnez "tcp_ip_socket" et cliquez "send" | |
Cassini renvoie l'index sélectionné: |
Notez que:
- nous avons effectué le chargement en plusieurs fois. En fait, il suffit d'initialiser le tableau et appeler DataBind une seule fois dans Page_Load, et seulement pour la première requête (IF NOT IsPostBack)
- pour le bouton "submit", il n'y a pas besoin de créer un événement qui "enverrait" la requête vers Cassini: l'émission fait partie intégrante de la mécanique CGI .HTML: si une <FORM> contient un bouton, son click envoie
une requête vers le Serveur
5.2 - ListBox et Tableau d'Objets Nous avons utilisé un tableau de String. En fait, la tListBox se lie à
n'importe quelle données qui implémente l'INTERFACE iList. Or un tableau implémente cette INTERFACE. Il faut un certain temps aux développeurs Delphi
pour réaliser que, même si la syntaxe est celle de Pascal, nous travaillons en réalité en Java. Or en Java, un entier est un objet, un réel est un objet, un tableau est un objet. Un ARRAY OF est donc plus proche d'une tList que d'un
paquet d'octets contenant les cellules en mémoire. Notre tableau contenait des Strings (donc des Objets String). Mais nous pouvons placer dans les cellules d'autres données descendant de tObject. Pour cela, il faut:
- que les cellules soient des Objets
- que les champs soient des PROPERTYes PUBLIC
Nous avons donc créé une UNITé contenant une CLASSe c_training que voici:
unit u_c_training; interface
type c_training= class
Public
m_id: Integer;
m_title: System.String;
m_price: Double;
Constructor create_training(p_id: Integer;
p_title: System.String; p_price: Double);
property id : Integer read m_id write m_id;
property title : System.String read m_title write m_title;
property price : Double read m_price write m_price;
end; // c_training
implementation
Constructor c_training.create_training(p_id: Integer;
p_title: System.String; p_price: Double);
begin Inherited Create;
m_id:= p_id;
m_title:= p_title;
m_price:= p_price;
end; // create_training end |
Pour relier la tListBox: - nous affectons à tListBox.DataSource le tableau m_c_training_array
- nous associons tListBox.DataTextField et la propriété "title" de chaque
objet. DataTextField indique quel champ sera affiché
- nous associons tListBox.DataValueField et la propriété "id" de chaque objet. DataValueField indique quel champ sera envoyé comme clé
Par conséquent:
| créez une nouvelle application Asp.Net, p_listbox_array_binding_3 et compilez |
| tapez l'unité u_c_training présentée ci-dessus, et importez-la dans la liste des USES de p_listbox_array_binding_3 | |
posez une tListBox sur la Forme | | dans la zone PUBLIC de t_31_listbox_array_binding_form déclarez le tableau:
m_c_training_array: array of c_training; | |
| dans tForm.PageLoad remplissez le tableau et liez les données:
procedure t_31_listbox_array_binding_3.Page_Load(sender: System.Object;
e: System.EventArgs);
var l_training_index: Integer;
procedure add_a_training(p_id: Integer; p_customer: System.String; p_amount: Double);
var l_c_training: c_training; begin
l_c_training:= c_training.create_training(p_id, p_customer, p_amount);
m_c_training_array[l_training_index]:= l_c_training;
Inc(l_training_index); end; // add_a_training
begin // Page_Load
if not IsPostBack
then begin
SetLength(m_c_training_array, 5);
l_training_index:= 0;
add_a_training(201, 'Asp_net', 1400.00);
add_a_training(202, 'ado_net', 1400.00);
add_a_training(203, 'delphi_2006_net', 2400.00);
add_a_training(204, 'win32_to_.net',1400.00);
add_a_training(205, 'tcp_ip_sockets', 1400.00);
Listbox1.DataSource:= m_c_training_array;
ListBox1.DataTextField:= 'title';
ListBox1.DataValueField:= 'id';
DataBind; end;
end; // Page_Load | | |
posez un tButton sur la Forme, appelez le "submit_button" | | pour visualiser l'élément de la ListBox sélectionnez,
- posez un tLabel sur la Forme
- créez l'événement ListBox1.SelectedIndexChanged et affichez le contenu de l'item sélectionné
procedure t_31_listbox_array_binding_3.ListBox1_SelectedIndexChanged(sender: System.Object;
e: System.EventArgs); begin
Label1.Text:= 'selected value: '
+ ListBox1.Items[ListBox1.SelectedIndex].ToString;
end; // ListBox1_SelectedIndexChanged | |
| compilez et exécutez | | Cassini envoie une page avec la ListBox remplie:
| | sélectionnez "win32_to_.net" et cliquez "send" |
| Cassini renvoie l'index sélectionné: |
Voici le contenu des paquets échangés: | le Client demande la page (le détail des en-têtes HTTP et le détail du
_VIEWSTATE codé ont été en partie supprimés ou abrégés)
-> | GET /p_31_listbox_array_binding_3/a_31_listbox_array_binding_3.aspx HTTP/1.1 | |
| Cassini renvoie la page <- | HTTP/1.1 200 OK ...
<- | <html> <- | <body>
<- | <input type="hidden"
name="__VIEWSTATE" value="dDwtNTY ...VmDK4=" />
<- | <select name="ListBox1" size="4" id="ListBox1" style="height:132px;width:105px;">
<- | <option value="201">Asp_net</option>
<- | <option value="202">ado_net</option>
<- | <option value="203">delphi_2006_net</option>
<- | <option value="204">win32_to_.net</option>
<- | <option value="205">tcp_ip_sockets</option>
<- | </select>
<- | <input type="submit" name="submit_button" value="send" id="submit_button" />
<- | <span id="Label1">Label</span>
<- | </form> <- | </body>
<- | </html> <- | | |
| l'utilisateur sélectionne "win32_to_.net" et clique "send"
-> | POST /p_31_listbox_array_binding_3/ a_31_listbox_array_binding_3.aspx HTTP/1.1 ... -> | __VIEWSTATE=dDwtN...AWsVmDK4%3D&ListBox1=204&submit_button=send
| | | Cassini renvoie la valeur du Label
<- | HTTP/1.1 200 OK ...
<- | <input type="hidden" name="__VIEWSTATE" value="dDwtNTY ... JkfbgA==" />
<- | <select name="ListBox1" size="4" id="ListBox1" style="height:132px;width:105px;">
<- | <option value="201">Asp_net</option>
<- | <option value="202">ado_net</option>
<- | <option value="203">delphi_2006_net</option>
<- | <option selected="selected" value="204">win32_to_.net</option>
<- | <option value="205">tcp_ip_sockets</option>
<- | <- | </select>
<- | <input type="submit" name="submit_button" value="send" id="submit_button" />
<- | <span id="Label1">selected value: win32_to_.net</span>
<- | </form> <- | </body>
<- | </html> | |
Et:
5.3 - ListBox et Base de Données
Maintenant que nous savons afficher des données dans une tListBox, l'étape suivante est de peupler la tListBox depuis une base de données. Pour cela, il suffit de remplir un tDataTable à partir d'une base de données,
puis de lier: - tListBox.DataSource et tDataTable.DefaultView
- tListBox.DataTextField et la colonne à afficher
- tListBox.DataValueField et la colonne de référence (code)
Nous remplirons la tDataTable en utilisant du code Ado.Net standard: - BdpConnection ouvrira la connexion
- BdpDataAdapter
- contiendra la commande SELECT
- remplira un tDataSet par l'appel tDataAdapter.Fill(tDataSet, nom_table)
Ces explications sont plus que succintes. Nous pourrions faire un tutorial
Ado.Net ici, mais la dernière fois que nous avons présenté un tutorial, il y en a eu pour 268 K. Nous nous permettrons donc de suggérer au lecteur intéressé par l'architecture Ado.Net à consulter ce
Tutorial Ado.Net. Nous utiliserons pour notre exemple le serveur Interbase, livré par défaut
avec Delphi. Nous allons commencer pour ce premier exemple par récupérer les données du serveur en code pur. Les étapes de connexion, chargement et liaison seront ainsi clairement identifiées. Voici donc notre exemple:
| créez une nouvelle application Asp.Net, p_listbox_database_binding_2 et compilez | |
créez un répertoire c:\programs\fr\asp_net\tutorial\_data\ et copiez-y le fichier EMPLOYEE.GDB, figurant, normalement en C:\Program Files\Fichiers communs\Borland Shared\Data\
Cela nous permettra de modifier les tables sans état d'âme | | posez une tListBox sur la Forme |
| posez un tButton sur la Forme, appelez le "submit_button". Créez son événement OnClick et chargez les données de la table DEPARTMENT dans Listbox1:
const k_database_path= 'C:\Programs\fr\asp_net\tutorial\_data\';
k_database_full_file_name= k_database_path+ 'employee.gdb';
k_user_password= 'Username=SYSDBA;Password=masterkey';
k_assembly= 'assembly=Borland.Data.Interbase, Version=2.5.0.0, '
+ 'Culture=neutral, PublicKeyToken=91d62ebb5b0d1b1b';
k_vendor_client= 'vendorclient=gds32.dll';
k_provider= 'provider=Interbase'; k_connection_string=
'Database='+ k_database_full_file_name
+ ';'+ k_user_password
+ ';'+ k_assembly
+ ';'+ k_vendor_client
+ ';'+ k_provider ;
k_select= 'Select * from department';
procedure t_32_listbox_databinding_2.submit_button_Click(sender: System.Object;
e: System.EventArgs);
var l_c_connection: BdpConnection;
l_c_data_adapter: BdpDataAdapter;
l_c_data_set: DataSet;
l_c_data_table: DataTable; begin
l_c_connection:= BdpConnection.Create(k_connection_string);
l_c_data_adapter:= BdpDataAdapter.Create(k_select, l_c_connection);
l_c_data_set:= DataSet.Create();
l_c_data_adapter.Fill(l_c_data_set, 'department');
l_c_data_table:= l_c_data_set.Tables['department'];
l_c_connection.Open();
ListBox1.DataSource:= l_c_data_table.DefaultView;
ListBox1.DataTextField:= 'Location';
ListBox1.DataValueField:= 'Dept_No'; DataBind;
l_c_connection.Close(); end; // submit_button_Click |
| | compilez et exécutez | |
Cassini envoie une page avec la ListBox vide: | | cliquez "send" |
| Cassini charge la Listbox et renvoie la page: |
Notez que - comme pour le chargement de la Listbox à partir d'un tableau, le chargement à partir d'une table se fait une seule fois. Il n'y a pas de liaison permanente avec la base de données. En effet, la page part à l'autre bout du
monde, et une requête de soumission ne viendra peut être jamais
C'est pourquoi nous pouvons ouvrir la connexion juste avant le chargement, et la refermer juste après ce chargement.
- notez aussi que pour les stages j'ai eu la désagréable surprise de voir que des applications développées sur une machine ne fonctionnaient pas sur un autre poste ayant une version différente des composants de bases de données.
Pour cette présentation, nous avions commencé les exemples en Delphi 2005, puis comme nous avons actuellement Delphi 2006, nous avons utilisé cette
nouvelle version pour le tutorial. Ici aussi, Asp.Net ne fut pas toujours d'accord.
Pour ces exemples simples, la solution fut simplement de recréer un exemple "bis". Un solution moins radicale consiste à effacer tous les .DCUIL, et,
dans le cas des base de données, de bien vérifier les chaînes de connexion. Dans certains cas j'ai aussi effacé la référence à l'Assembly de données Borland dans le .DPR: - Delphi 2005 référence
{%DelphiDotNetAssemblyCompiler 'c:\program files\fichiers communs\borland shared\bds\shared assemblies\3.0\Borland.Data.Provider.dll'}
- le BDS de 2006 a un numéro d'assembly 4.0
- dans l'exemple actuel, nous avons récupéré la chaîne de connexion en tirant-glissant une BdpConnection provenant du DataExplorer sur la Forme.
Vous retrouverez d'ailleurs cette BdpConnection1 dans le .ZIP des sources
5.4 - Repeater Asp.Net propose toute une série de composants capables de formatter des lignes de données avec - en-tête de liste
- lignes répétitives
- pied de liste
Ayant vu comment était traité la Listbox, il n'y a trop rien de surprenant à généraliser cette technique de mise en forme de lignes de données
Le principe de ces composants est la suivante: - fournit les données. Dans notre cas, nous les utiliserons depuis la table DEPARTMENT
- l'essentiel du code consiste à définir l'en-tête, la répétition, et le pied
de table. Et cela se fait au niveau du texte .ASPX (pas les controles et le code .PAS)
Voici le projet: |
créez une nouvelle application Asp.Net, p_33_repeater et compilez | | vérifiez (ou créez et copier) que EMPLOYEE.GDB est bien dans
c:\programs\fr\asp_net\tutorial\_data\ | | créez une nouvelle connexion dans l'Explorateur de base de données:
- dans la partie supérieure gauche, sélectionnez l'onglet "DataExplorer"
- effectuez un click droit souris sur "Interbase" et sélectionnez "Add New
Connection"
- dans le dialogue de nouvelle connexion, tapez le nom de la nouvelle connexion, par exemple "asp_tutorial"
- dans l'explorateur, sur "Interbase | asp_tutorial", cliquez le bouton droit et sélectionnez "modify connection"
- dans l'éditeur de connexion, initialisez le chemin de EMPLOYEE.GDB, le nom d'utilisateur et le mot de passe
- cliquez "test" pour vérifier la connexion et fermez
| |
dans le DataExplorer, sélectionnez la connexion "asp_tutorial" et tirez la sur la Forme | |
Delphi pose un BdpConnection1 dans la zone des composants non-visuels | | à titre de vérification, effectuez un clic droit souris sur
BdpConnection1, sélectionnez "connection editor" et cliquez "test" pour vérifier que la connexion est correcte | | posez un repeater sur la Forme
| |
posez un tButton sur la Forme, créez son événement OnClick, et faites-lui remplir Repeater1:
procedure T_33_repeater_form.submit_button_Click(sender: System.Object;
e: System.EventArgs);
var l_c_data_adapter: BdpDataAdapter;
l_c_data_set: DataSet;
l_c_data_table: DataTable; begin
l_c_data_adapter:= BdpDataAdapter.Create(k_select, BdpConnection1);
l_c_data_set:= DataSet.Create();
l_c_data_adapter.Fill(l_c_data_set, 'department');
l_c_data_table:= l_c_data_set.Tables['department'];
BdpConnection1.Open();
Repeater1.DataSource:= l_c_data_table.DefaultView;
DataBind; BdpConnection1.Close();
end; // submit_button_Click | | |
à présent définissez le format des données à afficher dans le fichier .ASPX:
<%@ Page Language="c#" Debug="true"
Codebehind="a_33_repeater.pas" AutoEventWireup="false"
Inherits="a_33_repeater.T_33_repeater_form"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html> <head> <title></title>
</head> <body ms_positioning="GridLayout">
<form runat="server"> <asp:Button id="submit_button"
style="Z-INDEX: 1; LEFT: 350px; POSITION: absolute; TOP: 38px"
runat="server" text="send"></asp:Button>
<asp:Repeater id="Repeater1" runat="server"> <HeaderTemplate>
<table> <tr>
<td bgcolor=#B0B0FF>ID</td>
<td bgcolor=#B0B0FF>Département</td>
<td bgcolor=#B0B0FF>Ville</td>
</tr> </HeaderTemplate>
<ItemTemplate> <tr>
<td><%#((System.Data.DataRowView)Container.DataItem)["dept_no"]%>
</td>
<td><%#DataBinder.Eval(Container.DataItem, "department")%>
</td>
<td><%#DataBinder.Eval(Container.DataItem, "location")%>
</td>
</tr> </ItemTemplate>
<FooterTemplate> </table>
</FooterTemplate> </asp:Repeater> </form>
</body> </html> | | |
compilez et exécutez | | Cassini envoie une page avec juste le bouton "send" |
| cliquez "send" | | Cassini charge le repeater (partiel):
dont le contenu .HTML est le suivant:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head>
<title></title> </head>
<body ms_positioning="GridLayout">
<form name="_ctl0" method="post" action="a_33_repeater.aspx" id="_ctl0">
<input type="hidden" name="__VIEWSTATE" value="dDw ... Cn0A==" />
<input type="submit" name="submit_button" value="send" id="submit_button" style="Z-INDEX: 1; LEFT: 350px; POSITION: absolute; TOP: 38px" />
<table> <tr>
<td bgcolor="#B0B0FF">ID</td>
<td bgcolor="#B0B0FF">Département</td>
<td bgcolor="#B0B0FF">Ville</td>
</tr> <tr>
<td>000</td>
<td>Corporate Headquarters</td>
<td>Monterey</td>
</tr> <tr>
<td>100</td>
<td>Sales and Marketing</td>
<td>San Francisco</td>
</tr> ... </table>
</form> </body> </html> | |
Comme on pouvait s'en douter, les lignes ajoutées dans le fichier .ASPX servent de modèle pour la page .HTML
5.5 - DataGrid en Lecture
Le composant royal de traitement de données est la grille. En terme Asp.Net il s'agit d'un tDataGrid. Et le DataGrid est simplement connecté par sa propriété DataSource à tDataTable.DefaultView
Voici notre premier exemple: | créez une nouvelle application Asp.Net, p_34_datagrid_select et compilez |
| vérifiez ou créés la connexion asp_tutorial (voir ci-dessus) | |
posez une tDataGrid sur la Forme | |
dans le DataExplorer, sélectionnez la connexion "asp_tutorial" et tirez la sur la Forme | |
Delphi pose un BdpConnection1 dans la zone des composants non-visuels | | à titre de vérification, effectuez un clic droit souris sur
BdpConnection1, sélectionnez "connection editor" et cliquez "test" pour vérifier que la connexion est correcte | |
posez un tButton sur la Forme, créez son événement OnClick, et faites-lui remplir la DataGrid:
const k_select= 'Select * from department';
procedure T_34_datagrid_select_form.submit_button_Click(sender: System.Object;
e: System.EventArgs);
var l_c_data_adapter: BdpDataAdapter;
l_c_data_set: DataSet;
l_c_data_table: DataTable; begin
l_c_data_adapter:= BdpDataAdapter.Create(k_select, BdpConnection1);
l_c_data_set:= DataSet.Create();
l_c_data_adapter.Fill(l_c_data_set, 'department');
l_c_data_table:= l_c_data_set.Tables['department'];
BdpConnection1.Open();
DataGrid1.DataSource:= l_c_data_table.DefaultView;
DataBind; BdpConnection1.Close();
end; // submit_button_Click | | |
compilez et exécutez | | Cassini envoie une page avec juste le bouton "send" |
| cliquez "send" | | Cassini charge la DataGrid:
|
Fort simple, n'est-ce pas. Hélas, simplicité trompeuse. La dbGrid était déjà
un sujet de légitime fierté de Delphi 1 par rapport aux tableaux de tEdit du Visual Basic. Eh bien la DataGrid HTML Asp.Net a encore plus de possibilités,
de propriétés, d'options. Elle remplirait des livres entiers. Nous n'en sommes pas là, et nous contenterons de fournir deux autres exemples de son utilisation.
5.6 - Pagination
Comme vous pouvez le constater, la page .HTML ci-dessus affiche TOUTE les lignes. Nous pouvons afficher page à page en bridant le nombre de lignes affichées par page, et en mettant en place une mécanique d'appel des pages suivantes (ou
précédentes). En fait, Asp.Net fait tout le travail pour nous. Pour paginer, nous devons - remplir la page comme précédemment
- dans l'ébauche .ASPX, brider le nombre de lignes de la DataGrid par un attribut "pagesize"
- dans le code .PAS répondre à l'événement tDataGrid.PageIndexChanged en retournant à Cassini le nouvel index de début de page
Voici le projet:
| créez une nouvelle application Asp.Net, p_35_datagrid_page et compilez | |
vérifiez ou créés la connexion asp_tutorial (voir ci-dessus) | | dans le DataExplorer, sélectionnez la connexion "asp_tutorial" et tirez la
sur la Forme | | Delphi pose un BdpConnection1 dans la zone des composants non-visuels |
| posez une tDataGrid sur la Forme | |
dans la partie PUBLIC de T_35_datagrid_page déclarez une méthode qui remplira les lignes de DataGrid1.
T_35_datagrid_page = class(System.Web.UI.Page)
...
public
procedure fetch_data;
end; // T_35_datagrid_page |
et écrivez le code qui remplit la grille (similaire aux méthodes précédentes):
const k_select= 'Select dept_no, department, location from department';
procedure T_35_datagrid_page.fetch_data;
var l_c_data_adapter: BdpDataAdapter;
l_c_data_set: DataSet;
l_c_data_table: DataTable; begin
l_c_data_adapter:= BdpDataAdapter.Create(k_select, BdpConnection1);
l_c_data_set:= DataSet.Create();
l_c_data_adapter.Fill(l_c_data_set, 'department');
l_c_data_table:= l_c_data_set.Tables['department'];
BdpConnection1.Open();
DataGrid1.DataSource:= l_c_data_table.DefaultView;
DataBind; BdpConnection1.Close();
end; // fetch_data | | |
sélectionnez DataGrid1, créez son événement PageIndexChanged:
procedure T_35_datagrid_page.DataGrid1_PageIndexChanged(source: System.Object;
e: System.Web.UI.WebControls.DataGridPageChangedEventArgs);
begin DataGrid1.CurrentPageIndex:= e.NewPageIndex;
fetch_data; end; // DataGrid1_PageIndexChanged | |
| dans le fichier .ASPX (ou dans la zone d'édition .HTML située sous la Forme), bridez le nombre de lignes à 4:
<html> <head>
<title></title> </head>
<body ms_positioning="GridLayout"> <form runat="server">
<asp:DataGrid id="DataGrid1"
style="Z-INDEX: 1; LEFT: 22px; POSITION: absolute; TOP: 14px"
runat="server" allowpaging="True"
pagesize="4"> </asp:DataGrid> </form>
</body> </html> | |
| compilez et exécutez | | Cassini affiche la première page de 4 lignes de la table |
| en dessous de la grille, cliquez ">" | | Cassini affiche les 4 lignes suivantes:
|
La position courante est gérée dans des champs cachés calculés en utilisant
JavaScript (parce que nous avons autorisé JavaScript sur Internet Explorer). Voici ce que Cassini a renvoyé en réponse à la première requête:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html> <head> <title></title>
</head> <body ms_positioning="GridLayout">
<form name="_ctl0" method="post" action="a_35_datagrid_page.aspx" id="_ctl0">
<input type="hidden" name="__EVENTTARGET" value="" />
<input type="hidden" name="__EVENTARGUMENT" value="" />
<input type="hidden" name="__VIEWSTATE" value="dDw2ODM5M...4zShk=" />
<script language="javascript" type="text/javascript"> <!--
function __doPostBack(eventTarget, eventArgument) {
var theform;
if (window.navigator.appName.toLowerCase().indexOf("microsoft") > -1)
{ theform = document._ctl0;
} else
{
theform = document.forms["_ctl0"];
}
theform.__EVENTTARGET.value = eventTarget.split("$").join(":");
theform.__EVENTARGUMENT.value = eventArgument; theform.submit();
} // --> </script>
<table cellspacing="0" rules="all" border="1" id="DataGrid1" style="border-collapse:collapse;Z-INDEX: 1; LEFT: 22px; POSITION: absolute; TOP: 14px">
<tr>
<td>DEPT_NO</td><td>DEPARTMENT</td><td>LOCATION</td>
</tr> <tr>
<td>000</td><td>Corporate Headquarters</td><td>Monterey</td>
</tr> <tr>
<td>100</td><td>Sales and Marketing</td><td>San Francisco</td>
</tr> <tr>
<td>600</td><td>Engineering</td><td>Monterey</td>
</tr> <tr>
<td>900</td><td>Finance</td><td>Monterey</td>
</tr> <tr>
<td colspan="3">
<span><</span>
<a href="javascript:__doPostBack('DataGrid1$_ctl8$_ctl1','')">></a>
</td> </tr>
</table> </form> </body>
</html> | En gros: - le clic sur ">" (7ième ligne avant la fin) appelle la fonction Javascript doPostBack
- cette fonction va utiliser les champs __EVENTTARGET et __EVENTTARGUMENT pour renvoyer à Cassini les paramètres du nouveau paquet de lignes
5.7 - Modification de Données
Nous pouvons aussi modifier les données présentées dans une Datagrid. Dans les exemples précédents, les données de la tDataTable Ado.Net étaient
présentées sous forme de texte dans une <TABLE> .HTML. Ces données sont donc en lecture pure. Pour pouvoir laisser l'utilisateur taper du texte, il faut obligatoirement
passer par des Edit .HTML et envoyer le texte tapé par l'utilisateur vers le Serveur. C'est ce que nous avons présenté dans l'article
Gestion Base de Données CGI. Dans les programmes qui permettaient la modification de données d'une Table Interbase
- notre page .HTML contenait des Edit .HTML
- un bouton "UPDATE" envoyait le contenu des ces Edit sous forme de paramètres CGI
- le programme CGI récupérait ces paramètres pour mettre à jour la base en
utilisant une requête SQL UPDATE
Eh bien, en Asp.Net c'est exactement la même chose, sauf que nous n'avons pas besoin de générer la page avec les Edit. De façon plus détaillée:
- nous ajoutons une tDataGrid à la Forme
- nous ajoutons une colonne spéciale qui permettra de récupérer une ligne avec des Edit
- l'utilisateur modifie les valeurs des Edit
- lorsque la page est soumise au Serveur, le code .PAS récupérera les données modifiées, et se chargera de mettre à jour la Table Interbase
Comme cette mécanique est un peu inhabituelle, nous allons d'abord présenter le
résultat, et ensuite le code. Voici donc les étapes, vu côté utilisateur: | l'utilisateur demande la page |
| Cassini renvoie une page, avec la DataGrid dans laquelle une colonne contient un lien "edit": | | l'utilisateur décide de changer "San Francisco" en "Los Angeles". Il clique le lien "Edit" |
| Cassini lui renvoie une nouvelle page où les champs de la ligne "San Francisco" ont été placés dans des Edit, et "Edit" a été remplacé par "Update / Cancel".
-at_53_datagrid_edit_answer_2 | |
L'utilisateur modifie les valeurs dans les boîtes d'édition. Dans notre cas il tape "Los Angeles": Puis il soumet la mise à jour en cliquant "Update" |
| la modification est soumise à Cassini, qui met à jour la Table SQL et renvoie les données modifiées:
|
En résumé, le mécanisme est identique à notre code CGI manuel, sauf que c'est
Asp.Net qui a placé des liens "edit" ou "update cancel" dans une colonne spéciale de la tDataGrid.
Voici donc comment code cet exemple: |
créez une nouvelle application Asp.Net, p_36_datagrid_edit_2 et compilez | | vérifiez ou créés la connexion asp_tutorial (voir ci-dessus) |
| dans le DataExplorer, sélectionnez la connexion "asp_tutorial" et tirez la sur la Forme |
| Delphi pose un BdpConnection1 dans la zone des composants non-visuels | |
posez une tDataGrid sur la Forme | | dans la partie PUBLIC de T_35_datagrid_page déclarez une méthode qui
remplira les lignes de DataGrid1.
T_36_datagrid_edit_2_form = class(System.Web.UI.Page)
...
public
procedure select_data;
end; // T_36_datagrid_edit_2_form
| et écrivez le code qui remplit la grille (similaire aux méthodes précédentes):
const k_select= 'Select dept_no, department, location from department';
procedure T_36_datagrid_edit_2_form.select_data;
var l_c_data_adapter: BdpDataAdapter;
l_c_data_set: DataSet;
l_c_data_table: DataTable; begin
l_c_data_adapter:= BdpDataAdapter.Create(k_select, BdpConnection1);
l_c_data_set:= DataSet.Create();
l_c_data_adapter.Fill(l_c_data_set, 'department');
l_c_data_table:= l_c_data_set.Tables['department'];
DataGrid1.DataKeyField:= 'dept_no';
DataGrid1.DataSource:= l_c_data_table.DefaultView;
BdpConnection1.Open(); DataBind;
BdpConnection1.Close(); end; // select_data |
| | dans l'événement tPage.Page_Load, chargez la table:
procedure T_36_datagrid_edit_2_form.Page_Load(sender: System.Object;
e: System.EventArgs); begin
if not IsPostBack
then select_data; end; // Page_Load |
| | sélectionnez DataGrid1. Dans le fichier .ASPX (ou dans la zone d'édition .HTML située sous la
Forme), ajoutez au milieu de <ASP:Datagrid> </ASP:Datagrid> ajoutez la balise qui permettra la génération de la colonne "edit / update / cancel" (tout et y compris <Column> ... </Column>):
<%@ Page language="c#" Debug="true"
Codebehind="a_36_datagrid_edit_2.pas" AutoEventWireup="false"
Inherits="a_36_datagrid_edit_2.T_36_datagrid_edit_2_form" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html> <head> <title></title>
</head>
<body ms_positioning="GridLayout" style="WIDTH: 653px; HEIGHT: 271px">
<form runat="server">
<ASP:DataGrid id="DataGrid1" style="Z-INDEX: 3; LEFT: 14px; POSITION: absolute; TOP: 14px" runat="server">
<Columns>
<asp:EditCommandColumn edittext="Edit1" updatetext="Update" canceltext="Cancel"
headertext="Edit"> </asp:EditCommandColumn>
</Columns> </ASP:DataGrid> </form>
</body> </html> | | |
A présent les événements. Sélectionnez DataGrid1, créez son événement EditCommand:
procedure T_36_datagrid_edit_2_form.DataGrid1_EditCommand(source: System.Object;
e: System.Web.UI.WebControls.DataGridCommandEventArgs);
begin
DataGrid1.EditItemIndex:= e.Item.ItemIndex;
select_data; end; // DataGrid1_EditCommand |
et son événement CancelCommand:
procedure T_36_datagrid_edit_2_form.DataGrid1_CancelCommand(source: System.Object;
e: System.Web.UI.WebControls.DataGridCommandEventArgs);
begin DataGrid1.EditItemIndex:= -1; select_data;
end; // DataGrid1_CancelCommand | finalement, updateCommand:
procedure T_36_datagrid_edit_2_form.DataGrid1_UpdateCommand(source: System.Object;
e: System.Web.UI.WebControls.DataGridCommandEventArgs);
var l_c_id_text_box, l_c_location_text_box: Textbox;
l_update_request: String;
l_c_command: BdpCommand; l_count: Integer;
begin select_data;
l_c_id_text_box:= e.Item.Cells[1].Controls[0] as TextBox;
l_c_location_text_box:= e.Item.Cells[3].Controls[0] as TextBox;
l_update_request:= 'UPDATE department '
+ ' SET location ='''+ l_c_location_text_box.Text+ ''''
+ ' WHERE dept_no= '+ l_c_id_text_box.Text;
l_c_command:= BdpCommand.Create(l_update_request, BdpConnection1);
BdpConnection1.Open;
l_count:= l_c_command.ExecuteNonQuery();
BdpConnection1.Close; DataGrid1.EditItemIndex:= -1;
DataGrid1.DataBind; end; // DataGrid1_UpdateCommand |
| | compilez et exécutez |
Notez que:
- cet exemple est très intéressant. D'abord parce qu'il est très décevant. Pour quelqu'un habitué de taper à sa guise dans un dbGrid Delphi 6, voici qu'il faut demander la permission en cliquant le lien "edit". Cela surprend
- pouvons nous faire mieux ? Bien sûr: en codant tout nous mêmes:
- soit nous présentons une <TABLE> .HTML ne contenant que de Edit
- soit nous détectons la frappe clavier et changeons dynamiquement
l'affichage textuel en affichage dans un Edit, grâce à des scripts JavaScript
Dans les deux cas, il faut envoyer le résultat vers le Serveur, et cela nécessite un clic sur un bouton de soumission (ou sur un lien "update")
- Cela souligne une fois de plus que, quelle que soit la prestidigitation Asp.Net, la mécanique ne peut pas faire plus que l'antique .HTML ne le permet. Pour taper des données, il y a seulement des Edit. Un point c'est tout
- est-ce grave docteur ? Pas réellement. Le but essentiel des applications Web n'est pas de taper des données dans des dbGrid:
- soit nous gérons le contenu affiché dans une table, et donc il s'agit uniquement de lire des données
- pour la saisie, la plupart du temps il s'agit de saisie "fiche à fiche". Prenez le cas typique d'une commande Amazon: on vous demande de cliquer ici ou là pour sélectionner les livres, puis à la fin vous tapez adresse,
carte bancaire etc dans des Edits isolés
- mentionnons aussi que la mécanique Asp.Net ne prévoit pas l'insertion d'une nouvelle ligne. C'est à nous de coder cette insertion manuellement
6 - Améliorations 6.1 - Qu'avons nous appris ? Asp.Net est gigantesque. Nous avons tenu à présenter quelques exemples non triviaux pour vous permettre de goûter la chose. Quelques points semblent ressortir:
- pour gérer des applications Asp.Net, il vaut mieux être familier avec les librairies du Framework .Net. Ici, point de tList, tStringList, IntToStr.
Mais des tCollections, des ToString, des tStreamReader etc.
- nous avons présenté les exemples "à marche forcée": pour remplir chaque objectif, nous avons indiqué comment faire. Point de détail sur toutes les
possibilités ou alternatives, point de copie des innombrables tables de propriétés de l'aide Asp.Net. Une fois de plus notre objectif était de vous faire parcourir le plus de terrain possible pour prendre la mesure de
ce nouveau domaine.
L'autre conséquence est qu'il vous faudra vite devenir un expert dans la manipulation de l'aide Asp.Net. C'est là que vous trouverez les descriptions exhaustives et les syntaxes. Passez donc une heure ou deux à
cliquer ici, synchroniser là le contenu avec l'arborescence (l'icône <-> tout en haut), basculer de l'index au contenu etc. C'est là un investisement que vous ne regretterez pas. Et finalement, ce n'est pas compliqué du tout:
"Microsoft .Net Frameword | Reference | Class Library" et là vous êtes "in business" : System, System.Data, System.IO, System.Runtime.Remoting, System.Windows.Forms.
- en finale, Asp.Net ne fait que générer du code .HMTL. Du .HTML pur et dur. Donc il est préférable de comprendre la syntaxe .HTML, et encore mieux d'avoir un peu pratiqué CGI ou ISAPI. Voyez nos tutoriaux dans ce domaine.
6.2 - Ce qui n'a pas été présenté Asp.Net remplit des livres entiers. Forcémement nous n'avons pas pu tout aborder. En particulier: - la sécurité: la gestion des authentification et autorisation
- l'optimisation et les caches
- la configuration
- plus généralement une présentation détaillée des CLASSes et des événements mis en oe
uvre. En gros une analyse UML de la mécanique.
6.3 - Les Formations Comme disait Alfred Hitchcock dans ses séries policières, "et voici un mot de notre sponsor". Nous animons donc tous les mois des
Formation, avec tout particulièrement celles qui pourraient intéresser un développeur Asp.Net:
- Formation Asp.Net consacrée aux compétences à acquérir pour réaliser des applications Asp.Net
- formation Delphi 2005 / 2006 : une formation plus générale, qui couvre la librairie .Net, Ado Net et Asp.Net.
Elle concerne surtout les personnes désireuses de passer du monde Win32 au monde .Net, en insistant sur les deux points clé: les bases de données et la programmation Internet
Nous intervenons aussi en tant que
développeur Delphi, soit pour effectuer des transferts de technologie, réaliser des projets complets, ou appuyer des équipes sur des projets spécifiques.
7 - Télécharger le code source Delphi Vous pouvez télécharger: Ce .ZIP qui comprend:
- les fichiers .ASPX / .PAS, les pages et unités annexes eventuelles
- les fichiers de paramètres (le schéma et le batch de création)
- dans chaque .ZIP, toutes les librairies nécessaires à chaque projet (chaque .ZIP est autonaume)
Ces .ZIP ne modifient pas votre PC (pas de changement de la Base de Registre, de DLL ou autre). Pour supprimer le projet, effacez le répertoire. Notez que les chemins sont des chemins ABSOLUS. Pour ces applications, le
chemin de base est: C:\programs\fr\asp_net\tutorial\
Et pour utiliser ces exemples: - créez ce répertoire
- dézipez chaque example dans son sous-répertoire
- compilez et exécutez
Comme d'habitude: - nous vous remercions de nous signaler toute erreur, inexactitude ou problème de téléchargement en envoyant un e-mail à jcolibri@jcolibri.com. Les corrections
qui en résulteront pourront aider les prochains lecteurs
- tous vos commentaires, remarques, questions, critiques, suggestion d'article, ou mentions d'autres sources sur le même sujet seront de même
les bienvenus à jcolibri@jcolibri.com.
- plus simplement, vous pouvez taper (anonymement ou en fournissant votre e-mail pour une réponse) vos commentaires ci-dessus et nous les envoyer en
cliquant "envoyer" :
- et si vous avez apprécié cet article, faites connaître notre site,
ajoutez un lien dans vos listes de liens ou citez-nous dans vos blogs ou réponses sur les messageries. C'est très simple: plus nous aurons de visiteurs et de références Google, plus nous écrirons d'articles.
8 - Référénces Google vous fournira de nombreux liens pour des tutoriaux Asp.Net. Pour ma part, j'ai surtout consulté le livre de - Xavier PACHECO
Delphi for .NET - Developper's Guide Sams - ISBN 0 672 32443 - 1 - 2004 - 2004 - Ecrit pour Delphi 8, ce livre a le mérite de nous parler surtout du
Framework .Net (Ado.Net et Asp.Net), en évitant de nous expliquer le détail de BEGIN et END. Il reste, même pour Delphi 2006, un ouvrage que je recommande chaudement.
Tout l'inverse de livres Français que j'ai feuilletés à l'occasion. "Lu et Approuvé par Borland France". Pardi, j'ai regardé. Un fratras de copies des aides Delphi, de figures Microsoft, et, pour le chapitre sur ECO, carrément la
copie francisée d'un "White Paper" gratuit de Borland US d'Anthony RICHARDSON (Google: WhitePaperECOFirstApplication.pdf), plagié ici sans même citer l'auteur. Très très élégant. Le tout dans un style ampoulé qui fait franchement rigoler:
"c'est Ado.Net tel un Neptune qui règne en maître sur tous les flots de données". Et dire que l'intéressé passe son temps à parler de "professionnalisme". Etre
sérieux et se prendre au sérieux sont deux choses très différentes, et nul doute que le lecteur a depuis longtemps fait la différence entre un Colibri facétieux et un Merlan prétentieux.
9 - L'auteur
John COLIBRI est passionné par le développement Delphi et les applications de Bases de Données. Il a écrit de nombreux livres et articles, et partage son temps entre le développement de projets (nouveaux projets, maintenance, audit, migration BDE, migration Xe_n, refactoring) pour ses clients, le
conseil (composants, architecture, test) et la
formation. Son site contient des articles
avec code source, ainsi que le programme et le calendrier des stages de formation Delphi, base de données, programmation objet, Services Web, Tcp/Ip et
UML qu'il anime personellement tous les mois, à Paris, en province ou sur site client. |