|
IntraWeb Tutorial - John COLIBRI.
|
- résumé : comment créer ses pages Web de façon visuelle en utilisant
IntraWeb et Delphi. En particulier l'accès aux bases de données (BDE,
Interbase, ADO) via des Edit ou des grilles, en mode affichage ou edition
- mots clé : IntraWeb - Développement de site Web - conception Rad -
Programmation Internet - grille modifiable
- logiciel utilisé : Windows XP personnel, Delphi 2006 Win32, Intraweb 8
- matériel utilisé : Pentium 2.800 Mhz, 512 Meg de mémoire, 250 Giga disque
dur
- champ d'application : Delphi 5, Delphi 6, Delphi 7, Delphi 8, Delphi 2005,
Delphi 2006, Delphi 2007
- niveau : développeur Delphi et Internet
- plan :
1 - Présentation IntraWeb
Intraweb est un jeu de composants Delphi permettant de développer des
applications Web de façon visuelle: le développeur choisit ses composants sur
la Palette, les dépose sur la Forme, modifie ses propriétés dans
l'Inspecteur d'Objet, et effectue des traitements dans du code Pascal
(Delphi)
Depuis Delphi 2, Borland a fait de gros efforts pour nous offrir des
composants permettant de créer des sites Web. Ces composants (WebBroker,
WebSnap) étaient cependant surtout orientés vers la création de pages
dynamiques par code. La partie construction visuelle de la présentation de la
page (mise en page, image, couleurs) n'était pas possible visuellement.
Personne n'a compris pourquoi une société capable de poser et ajuster quelques
labels et boutons sur une tForm Windows a choisi de ne pas permettre de poser
quelques labels et boutons sur une future page .HTML.
C'est donc ce vide immense qui a été comblé par Intraweb, et nous allons
présenter l'utilisation de cette technique.
Nous allons utiliser Delphi 2006, mais ce que nous présentons pourrait être
réalisé avec les composants Intraweb disponibles pour les versions Delphi 5,
Delphi 6, Delphi 7, Delphi 8, Delphi 2005, Delphi 2006, Turbo Delphi et
Delphi 2007
2 - La Première Applications IntraWeb
Commençons par l'exemple simple de la calculette:
|
l'utilisateur tape l'adresse Web d'une page permettant de faire des
multiplications
|
|
son explorateur web lui présente une page avec 2 Edit et un bouton
|
|
l'utilisateur tape 2 nombre et clic le bouton
|
|
son explorateur lui renvoie le résultat
|
Nous allons donc construire une page Web
- avec deux Edit et un Bouton
- lorsque l'utilisateur cliquera le bouton, nous calculerons le résultat et
l'afficherons dans un label
Par conséquent
|
créez sur votre disque un dossier 01_calculator
|
|
lancez Delphi 2006
|
|
lancez le Wizard Intraweb en sélectionnant "File | New | Other | Intraweb
| Intraweb wizard"
L'emplacement des Wizards Intraweb dans les menus Delphi ainsi que le
contenu des différents dialogues varie avec les versions de Delphi, mais
le mécanisme reste le même
|
|
Intraweb présente un dialogue permettant spécifier le type d'application
Intraweb que vous souhaitez créer
|
|
les valeurs par défaut ("StandAlone", et "with Datamodule") conviennent.
Ajoutez simplement le chemin
C:\programs\fr\web\intraweb\01_calculator
et le nom de votre projet
p_01_calculator
et cliquez "Ok"
|
|
Intraweb crée 3 unités
- celle qui communiquera le Serveur Web
- celle qui gèrera les sessions
- et surtout l'unité correspondant à la page que verra l'utilisateur
Ces fichiers sont visibles dans le gestionnaire de projet:
|
|
sauvegardez le projet et ses unités avec des noms correspondant à
l'application dans le bon répertoire.
Cette étape peut être sautée, mais notre expérience de Delphi 2006 nous a
appris à effectuer cette sauvegarde avant de poursuivre. Donc sélectionnez
chaque unité tour à tour et sélectionnez "Save As" pour
- sauvegarder ServerController sous le nom de u_01_server_controller
- sauvegarder UserSessionUnit sous le nom de u_01_user_session_unit
- sauvegarder Unit1 sous le nom de u_01_calculator
en vérifiant que chaque fichier va bien dans notre répertoire
Puis, dans u_01_server_controler, dans le USES, changez le nom
UserSessionUnit en u_01_user_session_unit
|
|
Compilez pour vérifier que tout est cohérent
|
|
comme nous utilisons XP avec son pare feu Internet, la sécurité Windows
nous demande si nous acceptons exécuter ce programme
|
|
cliquez "Débloquer"
|
|
le Serveur IntraWeb est affiché
|
|
fermez ce Serveur (l'icône "x" en haut à droite)
|
|
de retour dans Delphi, sélectionnez la page u_01_calculator (pas le
contrôleur ou la session).
Puis dans la Palette, sélectionnez la page correspondant aux composants
visuels "IW Standard", puis sélectionnez un tIwEdit
et posez-le sur la Forme.
Posez-en un second, puis, de la même page de la Palette, un tIwButton et
un tIwLabel
|
|
La Forme aura l'allure suivante:
|
|
créez l'événement qui va calculer la multiplication en double-cliquant sur
IwButton1 (exactement comme pour une application Windows), et tapez le
code qui va effectuer le calcul
procedure TIWForm1.IWButton1Click(Sender: TObject);
begin
IwLabel1.Caption:=
IntToStr(StrToInt(IwEdit1.Text)* StrToInt(IwEdit2.Text));
end; // IWButton1Click
|
|
|
compilez
|
|
le Serveur Internet Intraweb est affiché:
|
|
lancez l'explorateur Web
- soit en cliquant sur l'icône désignée par la flèche rouge
- soit en tapant F9
- soit par le menu "File | Execute"
|
|
Internet Explorer est lancé, et notre page est présentée:
|
|
tapez deux nombres, par exemple 3 et 5 et cliquez "IwButton1"
|
|
le résultat est affiché dans Internet Explorer

|
Notez que:
- nous avons utilisé IntraWeb en mode "StandAlone": notre application
contient en fait un Serveur Web qui est chargé de fournir notre page à
Internet Explorer. Il existe un autre mode, dit mode "Page" dont nous
parlerons à la fin de notre présentation
- comme indiqué, l'emplacement dans les menus Delphi du Wizard qui lance le
serveur Standalone peut varier énormément selon la version de Delphi. Mais
en naviguant un peu dans ces menus, il n'est guère difficile de trouver la
page Intraweb, et sur cette page une icône correspondant au mode Standalone
- lorsque nous exécutons le projet et lançons le Serveur (F9 ou l'icône
verte), il ne faut pas trop tarder à remplir la page et cliquer sur le
bouton. Il y a en effet une temporisation qui peut bloquer la réponse. Dans
ce cas relancez l'exécution Delphi
Nous allons à présent examiner le principe du fonctionnement Intraweb, puis
présenter quelques exemples d'applications simples utilisant Intraweb.
3 - Le fonctionnement Intraweb
3.1 - Fonctionnement Internet
Un petit rappel rapide sur le fonctionnement des sites Web:
|
un développeur écrit des fichiers avec une syntaxe spéciale (.HTML)
correspondant à ses pages. Voici par exemple une page simple avec un texte
de bienvenue:
<HTML>
<BODY>
Bienvenue sur notre site
</BODY>
</HTML>
|
La page, qui peut être rédigée en utilisant NotePad (ou tout autre éditeur
de texte ASCII) est sauvegardée sur disque, sous le nom de HELLO.HTML, par
exemple
|
|
le développeur lance un Serveur Web, qui est une application qui écoute et
attend les requêtes provenant de clients. Ce serveur peut être IIS
(Internet Information Server), Apache, un serveur Web écrit en
utilisant des Sockets Delphi:
|
|
un utilisateur lance un explorateur web (Internet Explorer, Netscape, un
client Web écrit avec des Sockets Delphi, ...), et demande à
l'explorateur web de rapporter la page HELLO.HTML:
|
|
le Serveur Web localise HELLO.HTM, l'envoie vers l'explorateur, qui
l'affiche:
|
3.2 - Fonctionnement Intraweb en mode Application
Si nous utilisons Intraweb,
- la construction du texte .HTML de la page sera fait de façon visuelle en
posant des composants sur la Forme. Les propriétés de ces composants seront
utilisées par Intraweb pour générer le fichier .HTML
- le Serveur Web est un Le fichier .HTML résultant
Ceci peut être schématisé ainsi:
|
le développeur charge Delphi avec les composants Intraweb:
|
|
en utilisant la Palette, l'Inspecteur d'Objet et les Formes, il
construit ses pages et compile l'application, qui est placée sur disque,
dans un fichier nommé, par exemple, HELLO.EXE:
|
|
cette application est lancée, et elle correspond à un Serveur Web
Intraweb, plus les paramètres des pages construites:
|
|
un utilisateur lance un explorateur web (Internet Explorer, Netscape, un
client Web écrit avec des Sockets Delphi, ...), et demande à
l'explorateur web de rapporter la page HELLO.HTML:
|
|
le Serveur Web Intraweb utilise les paramètres de la Forme pour
construire la page HELLO.HTML, et la retourne au client qui l'affiche:
|
La génération d'une page .HTML à partir d'un Forme n'est en fait pas très
complexe: il suffit d'analyser les propriétés des contrôles et générer les
balises .HTML (<BODY>, <INPUT> etc) correspondantes. Nous avons d'ailleurs
présenté dans l'article Delphi Web Designer
un projet Delphi permettant de générer une page .HTML à l'aide de contrôles
Delphi (tPanel, tEdit, tLabel ...) posés sur une tForm. Notre générateur
est bien entendu très simplet par rapport aux contrôles IntraWeb, mais il
permet de comprendre le principe. De plus, Intraweb a ajouté le Serveur Web,
et même le lancement de l'Explorateur Web.
3.3 - Architecture IntraWeb en mode Application
Voici la structure d'un projet IntraWeb en mode Application:
et:
- nous utiliserons le Serveur Web IntraWeb (plutôt que les composants
WebBroker)
- ce Serveur est construit à l'aide des composants Indy. Ceci explique
d'ailleurs les possibilités SSL (Secure Socket Layer, ou encore mode
TCP/IP sécurisé) offertes par Intraweb
- IwServerController est chargé de gérer l'application, et en particulier
- les sessions utilisateur
- les exceptions
Voici un diagramme de classe UML simplifié (les classes Intraweb
représentées sont celles de Delphi 6):
Les pages sont appelées par un Explorateur Web en utilisant une URL ayant
la structure suivante (visible sur la capture d'écran ci-dessus):
et:
- le port est spécifié par tIwServerController.Port
- le type de commande est géré par tIwServerController.ExecCmd
- chaque session a un identificateur (calculé, entre autres, à l'aide de la
date), utilisé par les cookies ou les champs cachés
- tIwAppForm est l'ancêtre de toutes nos formes. Cette classe sert de
conteneur pour tous les contrôles et permet de définir les propriétés des
pages. Le diagramme de classe simplifié est le suivant:
- pour mettre en page nos Formes, tIwAppForm utilise un descendant de
tIwLayoutMgr
Si aucun tIwLayoutManager n'est placé sur la Forme, un gestionnaire de
position est créé par défaut.
3.4 - Forme CGI
Nos schémas ci-dessus ont présenté la mécanique de requête de page par un
Explorateur, et la réponse .HTML du Serveur Web.
Lorsque la page .HTML contient des zones de saisie (texte, checkbox, bouton
radio, listbox), il y a un double aller-retour entre l'Explorateur et le
Serveur.
Tout d'abord, il faut comprendre que la mécanique utilisée pour qu'un
utilisateur échange des informations avec un Serveur est l'antique technique
des <FORM> .HTML. Elle fonctionne ainsi:
|
le développeur place dans son texte .HTML une balise <FORM> à l'intérieur
de laquelle se trouvent des balises pour:
- au moins un bouton
- des contrôles de saisie. Par exemple un Edit
Voici une telle page pour convertir des Euros en Dollars:
et le texte .HTML correspondant est le suivant:
<HTML>
<HEAD>
</HEAD>
<BODY>
<FORM METHOD="POST"
ACTION="http://www.jcolibri.com/convert.exe">
valeur en euro ?
<INPUT TYPE="edit" NAME="les_euros"><BR>
<INPUT TYPE="submit" VALUE="convertis"><BR>
</FORM>
</BODY>
</HTML>
|
où
- <INPUT TYPE="edit" ...> est l'Edit
- <INPUT TYPE="submit" ...> est le bouton
- <FORM METHOD="POST" ACTION="http://www.jcolibri.com/convert.exe"> est la
balise qui définit le formulaire Web. Cette balise indique quel est le
logiciel sur le Serveur qui traitera la conversion: CONVERT.EXE
|
|
le développeur crée donc ce logiciel. Le contenu de ce type de programme a
été présenté dans l'article CGI Form. Ce type de
logiciel est donc placé sur le Serveur Web:
|
|
le Serveur Web est lancé (IIS, Netscape ...)
Voici alors la situation:
|
|
un utilisateur lance son Explorateur, et demande la page EURO.HTML:
|
|
le Serveur retourne la page avec la <FORM>:
|
|
l'utilisateur entre un montant (123, par exemple) et clique le bouton
"convertis"
|
|
le Serveur
- analyze les paramètres (la valeur 123)
- charge CONVERT.EXE et lui transmet les paramètres
- CONVERT.EXE effectue la conversion, et construit une nouvelle page
.HTML. Supposons que cette réponse contienne uniquement la valeur ne
dollars ($ 166). Cette page est renvoyée au Serveur
- le Serveur retourne cette page .HTML à l'utilisateur
et l'Explorateur affiche la réponse:
|
Le point extrêmement important est que le traitement du "clic" est effectué sur
le Serveur. Cette mécanique est chevillée dans le protocole .HTML, et est
identique que nous mettions en oeuvre CGI, ISAPI, NSAPI, ASP, ASP.NET ou ...
Intraweb. Pour résumer
- l'utilisateur récupère une <FORM> .HTML
- il remplit les différentes informations (Edit, CheckBox etc) et envoie les
données qu'il a fourni en cliquant un Bouton
- le Serveur récupère ces informations et en fait ce qu'il veut. En général,
il renvoie au moins un accusé de réception, ou bien retourne la même page
mise à jour, ou encore renvoie un autre page .HTML
Lorsque nous utilisons une page .HTML construite avec Intraweb et qui contient
des contrôles, la mécanique est la même, sauf que
- le Serveur Web est le Serveur Intraweb
- ce Serveur est utilisé pour envoyer toutes les pages .HTML: aussi bien la
page initiale, que les pages calculées en réponse aux valeurs retournées par
l'Explorateur clique sur un Bouton. Et c'est le code situé dans
l'événement OnClick du bouton qui est utilisé pour effectuer la suite des
traitements (mise à jour d'une base de données, intégration des valeurs dans
une réponse etc)
- tous les calculs se font donc sur le Serveur, et par du code Delphi usuel.
Vous avez donc accès à tout: les PROCEDUREs, les CLASSes, les UNITs,
les bases de données etc. Et dans un langage décent, pas dans un langage de
script à la syntaxe ahurissante.
Le schéma final devient dans ce cas:
Notez que:
- TOUTES nos pages, plus le Serveur Intraweb sont contenus dans un seul .EXE
(en mode Application).
Nous avons présenté la mécanique CGI pour bien illustrer le fonctionnement
sous-jacent. Mais vous n'aurez pas à programmer de CGI, ISAPI, d'écrire de
balises .HTML etc: c'est Intraweb qui mettra tout en place. C'est ce que nous
allons examiner à présent.
4 - Quelques Techniques IntraWeb
4.1 - Exemples IntraWeb
Maintenant que nous avons présenté un premier exemple ainsi que le
fonctionnement de base d'IntraWeb, nous allons examiner quelques exemples un
peu plus élaborés:
- navigation entre plusieurs pages - échange de données entre pages
- utilisation de contrôles liées aux bases de données (BDE, Interbase, ADO)
- affichage dans des tIwDbEdit, tIwDbGrid
- modification de tIwDbGrid
4.2 - Navigation entre plusieurs pages
4.2.1 - Transfert de page
Un site est en général composé de plusieurs pages .HTML, et l'utilisateur
navigue d'une page à une autre en utilisant des hyper-liens ou des boutons.
Dans le cas d'Intraweb
- nous créons autant de pages Intraweb additionnelles que nous souhaitons en
utilisant "File | New | Other | Intraweb | New Form"
- Intraweb gère une pile de pages actives, et
- au lancement c'est la page par défaut qui est ajoutée à la pile
- lorsque l'utilisateur clique un bouton, le Serveur prend la main, en
fait dans le OnClick du bouton de la page cliquée. Pour que le Serveur
renvoie une page vers l'utilisateur, il faut
- créer la seconde page
var my_iw_page: t_my_iw_page;
my_iw_page:= t_my_iw_page.Create(WebApplication);
|
- l'ajouter au sommet de la pile en appelant:
ce qui peut, dans ce cas de "création + affichage" être abrégé par:
|
t_my_iw_page.Create(WebApplication).Show
|
- lorsque nous souhaitons qu'une page ne soit pas retournée à
l'utilisateur, nous appelons:
et
- la page est retirée de la liste des pages actives
- la page située au sommet de la pile est retournée à l'utilisateur
Notez que:
- les nouvelles pages sont ajoutées par "File | New | Other | Intraweb | New
Form", PAS en ajoutant une nouvelle Forme Windows ("File | New | Form")
- pour retourner une nouvelle page, il FAUT utiliser iw_page.Show(), et
jamais ShowModal. De même pour supprimer une page, il faut utiliser
iw_page.Hide() (et pas Visible:= False). Ce sont en effet ces deux
primitives Show() et Hide() qui gèrent la pile des pages IntraWeb
4.2.2 - Exemple de transfert de page
Voici un exemple avec deux pages: la première calcule le taux du dollar, la
seconde renvoie la valeur.
Commençons par créer une application IntraWeb:
|
lancez une nouvelle application IntraWeb par "File | New | Other |
Intraweb | Intraweb Application Wizard"
|
|
le wizard est affiché
|
|
tapez le chemin et le nom de l'application, par exemple P_02_TWO_PAGES. et
cliquez "OK"
|
|
la nouvelle application est créée
|
|
modifiez aussi le nom de UNIT1 en U_02_TWO_PAGES_1, et le nom de tIwForm1
en tIwForm_page_1
|
|
compilez pour vérifier
|
Ajoutons la seconde page:
Garnissons la première page
|
sélectionnez U_02_TWO_PAGES_1
|
|
de l'onglet "IW Standard" de la Tools Palette, sélectionnez un tIwLabel
posez-le sur la Forme, et changez son Caption en "valeur en euros"
|
|
de l'onglet "IW Standard", sélectionnez un tIwEdit, et initialisez Text à
une valeur par défaut, par exemple 123
|
|
de l'onglet "IW Standard", sélectionnez un tIwButton, nommez-le "convert",
créez son événement OnClick, et calculez la valeur en dollar
(multiplication par 1.35 environ), et effectuez le transfert de page:
procedure TIWForm_page_1.convertClick(Sender: TObject);
var l_euro, l_dollar: Double;
l_c_iwform_page_2: tiwform_page_2;
begin
l_euro:= StrToFloat(IwEdit1.Text);
l_dollar:= l_euro* 1.35;
l_c_iwform_page_2:= tiwform_page_2.Create(WebApplication);
l_c_iwform_page_2.iwlabel1.Caption:= IwEdit1.Text
+ ' euros = '+ FloatToStr(l_dollar)+ ' US $';
l_c_iwform_page_2.Show();
// -- same as
// with tiwform_page_2.Create(WebApplication) do
// begin
// iwlabel1.Caption:= IwEdit1.Text+ ...ooo...
// Show;
// end;
end; // convertClick
|
Ajoutez aussi, après IMPLEMENTATION l'importation de la page 2:
implementation
uses u_02_two_pages_2;
|
|
Et pour que la seconde page permette de revenir à la page 1 pour entrer une
nouvelle valeur:
Notez que
- l'URL affiche un nombre d'aller et retour croissant: 0, 1, 2
- le SessionId en revanche reste le même
- pour une raison inconnue, nous ne pouvons pas utiliser des Name comportant
un souligné "_"
4.2.3 - Architecture de la pile des pages
Au niveau architecture, la CLASS tIwApplication gère les différentes pages
(formes):
et lorsqu'un nouvel utilisateur se connecte, tIwControllerBase lance une
nouvelle session, ce qui crée une nouvelle tIwApplication avec une nouvelle
liste de pages, et une page active:
et par la suite, cet utilisateur ajoute ou retire des pages actives à sa liste
par Show() et Hide().
4.2.4 - Création et Destruction
Show() et Hide() ne font que manipuler la présence de la page au sommet de la
pile, et ne gèrent ni la création, ni la suppression de l'objet.
Pour créer une page, il faut utiliser, comme nous l'avons indiqué ci-dessus:
|
t_my_iw_page.Create(WebApplication)
|
et pour libérer une page:
En fait dans l'exemple ci-dessus nous avons utilisé page_2.Hide(), alors que
nous aurions du employer page_2.Release()
Comme chaque page a un référence vers tIwApplication qui contient la liste des
pages, il est possible, à titre de debugging, de visualiser cette liste:
Maintenant que vous comprenez la mécanique, plusieurs solutions sont possibles:
- dans la seconde page, dans backbutton.Click, utiliser Release() au lieu
de Hide()
- placer la variable l_c_page_2 dans TIWForm_page_1, et ne créer la seconde
page que si cet attribut est NIL
- placer l_c_page_2 dans un tDataModule associé à la session (cf plus bas)
Notez aussi que
- une même page (instance de tIwForm_nnn) peut être référencée plusieurs fois
dans la pile des pages. Cela peu se produire lorsque l'application exige que
cette page soit présentées à plusieurs reprises après des visites à d'autres
pages. Si nous appelons Release(), toutes ces instances seront libérées
- une fois une tiwForm créée, elle existe tant que vous ne la supprimez pas
par t_iwForm.Release(). Il est donc très simple de transférer des données
d'une page à l'autre en accédant à ses contrôles (ce que nous avons fait en
initialisant page_2.Label depuis page_1.ButtonClick), ou à des attributs
PUBLIC
- comme l'ensemble des pages utilise des Threads, il ne faut PAS utiliser
des variables globales. Plusieurs alternatives sont possible
- utiliser les attributs des pages en mémoire
- utiliser les tDataModules associés à la session (cf plus bas)
- en dernier recours, utiliser des tThreadVars
4.2.5 - Lancement depuis un Client distant
Pour le moment nous avons affiché nos pages via le Serveur Standalone.
Naturellement nos utilisateurs seront sur une machine éloignée, et c'est eux
qui lanceront la demande d'une page de notre site.
En fait, il suffit dans Internet Explorer de fournir l'adresse et le port (si
ce n'est pas le port 80) pour obtenir la page principale, puis de naviguer à
partir de là.
Nous utilisons à l'Institut Pascal un réseau local, et le poste sur lequel est
le Serveur Intraweb a l'adresse IP 152.178.41.18 (lancez ipconfig.exe pour
éventuellement connaître l'adresse de votre poste). Pour effectuer notre essai,
il faut connaître le port. Par conséquent
4.3 - Bases de Données
4.3.1 - Un tdbEdit et une Table BDE (dBase)
Commençons par afficher dans des dbEdit les champs de la Table ANIMALS.DBF.
Nous allons
- placer les composants d'accès aux bases de données sur le tDataModule
UserSession
- initialiser la connection avec la base
- ajouter à la tIwForm quelques champs tIwDb_xxx
Par conséquent:
|
lancez une nouvelle application IntraWeb par "File | New | Other |
Intraweb | Intraweb Application Wizard"
|
|
le wizard est affiché
|
|
tapez le chemin et le nom de l'application, par exemple P_03_DBEDIT et
cliquez "OK"
|
|
la nouvelle application est créée
|
|
modifiez aussi le nom de UNIT1 en U_03_DBEDIT
|
|
compilez pour vérifier
|
Puis nous armons une Table
|
sélectionnez UserSessionUnit et sélectionnez la forme (onglet "Design" en
bas)
|
|
initialisez une tTable
- de l'onglet "BDE" de la Tool Palette, sélectionnez une tTable et
posez la sur la forme tIwUserSession
- dans l'Inspecteur
- initialisez DataBaseName avec DBDEMOS
- initialisez TableName avec ANIMALS.DBF
- basculez Active sur True
|
|
de l'onglet "DataAccess" de la Palette, sélectionnez une tDataSource et
posez la sur la forme tIwUserSession. Initialisez DataSet vers Table1
|
Ajoutons un tIwDbEdit pour afficher un champ:
- sélectionnez U_03_DBEDIT, sélectionnez le code
- pour que les composants db_xxx puissent accéder à DataSource1, ajoutez à
la clause USES l'importation de UserSessionUnit
- sélectionnez la forme
- sélectionnez de l'onglet "IwData" de la Palette, et dans cette page
sélectionnez un tiwDbEdit:
et posez-le sur la Forme
Dans l'Inspecteur
- sélectionnez DatSource. Si vous avez bien importé UserSessionUnit, alors
IWUserSession.DataSource1Data sera affiché. Sélectionnez cette source
- sélectionnez DataField, et choisissez un champ, par exemple AREA
- compilez et exécutez
- voici le résultat:

Tant qu'a faire, nous pouvons ajouter un navigateur et afficher quelques
images:
- de l'onglet "IwData" de la Palette, sélectionnez un tIwDbNavigator,
posez-le sur le Forme, et initialisez DataSource vers DataSource1
- de cette page sélectionnez un tiwDbImage, posez-le sur la Forme et
initialisez
- DataSource vers DataSource1
- DataField vers BMP
- compilez et exécutez
- voici le résultat:

4.4 - tIwDbGrid et la base de données Interbase
4.4.1 - Utilisation d'une DataGrid
Pour varier les plaisirs, voici une application avec une grille et un moteur
SQL Interbase. Tout d'abord, créons un nouveau projet:
|
lancez une nouvelle application IntraWeb par "File | New | Other |
Intraweb | Intraweb Application Wizard"
|
|
le wizard est affiché
|
|
tapez le chemin et le nom de l'application, par exemple P_04_DBGRID et
cliquez "OK"
|
|
la nouvelle application est créée
|
|
modifiez aussi le nom de UNIT1 en U_04_DBGRID_INTERBASE
|
|
compilez pour vérifier
|
Puis nous armons une Table, par exemple EMPLOYEE.COUNTRY
|
dans le Gestionnaire de Projet, sélectionnez UserSessionUnit et
sélectionnez la forme (onglet "Design" en bas)
|
|
initialisez une tTable
- de l'onglet "Interbase" de la Tools Palette, sélectionnez une
tIbDataBase et posez-la sur la Forme.
Cliquez deux fois sur IbDatabase1 pour ouvrir l'Editeur de
Connection, et initialisez ses propriétés vers votre base. Dans notre
cas:
- DataBase vers
C:\Program Files\Fichiers communs\Borland
Shared\Data\EMPLOYEE.GDB
- User Name à SYSDBA
- Password à masterkey
- Login à False
Testez, et validez par "Ok"
- de l'onglet "Interbase" de la Palette, sélectionnez une tIbTransaction
et reliez-la à IbDatabase1 et réciproquement
- de l'onglet "Interbase" de la Palette, sélectionnez une tIbQuery, et
initialisez
- DataBase vers IbDatabase1
- SQL avec une sélection de Table, par exemple
- basculez Active à True
- de l'onglet "Data Access" de la Palette, sélectionnez une tDataSource,
posez-la sur la tForm et initialisez DataSet vers IbQuery1
|
|
voici la vue de notre tDataModule:

|
Ajoutons un tIwDbGrid pour afficher la Table:
4.4.2 - Techniques d'affichage d'une tIwDbGrid
Nous pouvons utiliser plusieurs propriétés pour varier l'affichage dans une
tIwDbGrid:
- nous pouvons alterner les couleurs des lignes en utilisant
RowAlternateColor
- la couleur de la ligne au-dessus de laquelle passe la souris peut être
modifiée par RollOver, RollOverColor
- la couleur de la ligne sélectionnée
Voici quelques exemples:
4.4.3 - Pagination
Lorsque la Table est de grande taille, il est coûteux et inefficace de
transférer vers le client toutes les lignes. Nous pouvons limiter le nombre de
lignes envoyées en utilisant RowLimit, et en gérant par des boutons la demande
de la page précédente ou suivante.
Par exemple:
|
posez une autre tIwDbGrid sur la tForm, et
- liez DataSource vers DataSource1
- basculez FromStart à False
- donnez à RowLimit une valeur telle que 4
|
|
posez un tIwButton sur la Forme, créez son événement OnClick et tapez le
code qui déplace la ligne courante d'un nombre de lignes égale au nombre de
lignes de le tIwDbGrid:
procedure TIWForm1.next1Click(Sender: TObject);
begin
UserSession.IbQuery1.MoveBy(IwDbGrid3.RowLimit);
end; // next1Click
|
Importez aussi dans la liste des USES SERVERCONTROLLER pour pouvoir
accéder à USERSESSION.IbQuery1
|
|
posez de même un bouton pour reculer
|
|
compilez et exécutez
|
|
voici le résultat :

|
Notez que
- IbQuery1 est sur le DataModule USERSESSION. Comme indiqué plus haut, il
n'y a PAS de variable globale nous permettant de désigner ce DataModule.
En revanche, ServerController a créé une FUNCTION, appelée UserSession,
qui nous permet d'accéder aux composant de TIWUserSession
- vous pouvez aussi ajouter le code qui inhibe les boutons lorsque nous
dépassons les limites (IbQuery.Eof etc)
- le lecteur attentif aura remarqué que dans la dbGrid2, la couleur jaune
souligne bien que c'est "japan" qui est devenue la ligne courante
- comme nous ne pouvons utiliser un IwButton1.Name tel que "next_", nous
avons utilisé "next1". Le risque est en effet, dans des WITH éventuels,
d'utiliser le nom Next ayant une signification pour d'autres composants
faisant partie du WITH. Si vous n'employez pas de WITH, nommez les
boutons comme bon vous semble.
4.4.4 - tDbGrid modifiable
Nous allons examiner comment l'utilisateur peut changer les valeurs présentées
dans un grille. Nous allons en profiter pour montrer comment utiliser une
connection ADO.
Commençons par créer l'application:
|
lancez une nouvelle application IntraWeb par "File | New | Other |
Intraweb | Intraweb Application Wizard"
|
|
le wizard est affiché
|
|
tapez le chemin et le nom de l'application, par exemple P_05_DBGRID_ADO et
cliquez "OK"
|
|
la nouvelle application est créée
|
|
modifiez aussi le nom de UNIT1 en U_05_DBGRID_ADO
|
|
compilez pour vérifier
|
A présent armons une Table ADO
Ici, nous allons utiliser le fichier ACCESS fourni avec Delphi, appelé
DBDEMOS.MDB. Ce fichier est situé, dans notre cas, en
C:\Program Files\Fichiers communs\Borland Shared\Data\
et nous l'avons copié dans un répertoire plus confortable, appelé \_data,
proche de notre application
Voici comment utiliser une Table ADO:
|
sélectionnez le DataModule USERSESSIONUNIT, et sa tForm ("Design")
|
|
de l'onglet "DbGo" de la Tools Palette, sélectionnez tAdoConnection et
posez-le sur la tForm.
|
|
Dans l'Inspecteur, sélectionnez Connection et cliquez sur l'ellipse
|
|
l'Editeur de connection ADO de Microsoft est présenté.
|
|
nous allons construire notre propre chaîne de connection, et nous
sélectionnons "use connection string | Build"
|
|
le constructeur de chaînes de connection Microsoft est présenté
|
|
comme nous allons utiliser le moteur Sql ServerADO, nous sélectionnons
"Fournisseur | Microsoft 4.0 OleDb Provider", puis cliquons "Suivant"
|
|
la page "Connexion" est présentée
|
|
nous cliquons l'ellipse à côté de "sélectionner la base de données" et
allons sélectionner dbdemos.mdb.
|
|
nous cliquons "tester la connexion", qui réussit, et fermons l'éditeur par
"ok"
|
|
dans la Palette, nous sélectionnons un tAdoDataset et le posons sur la
tForm. Nous initialisons Connection vers AdoConnection1, puis
sélectionnons CommandText et cliquons sur l'ellipse
|
|
l'éditeur de requêtes est présenté
|
|
tapez la même requête que ci-dessus (dbdemos.mdb contient la même table que
employee.gdb):
|
|
basculez AdoDataSet.Active à True
|
|
pour pouvoir modifier la Table
- posez un tAdoCommand sur la Forme
- initialisez Connection vers AdoConnection1
- basculez CheckParams à False
|
Voici comment utiliser la Table ADO avec Intraweb:
|
sélectionnez U_02_EDIT_DBGRID_ADO
|
|
pour que les composants db_xxx puissent accéder à DataSource1, ajoutez à
la clause USES l'importation de UserSessionUnit
|
|
de l'onglet "Data Access" de la Palette, sélectionnez une tDataSource,
posez-la sur la tForm et initialisez DataSource1.DataSet vers
AdoDataSet1
|
|
de l'onglet "Iw Data", selectionnez une tIwDbGrid et posez-la sur la
Forme. Sélectionnez DatataSource et initialisez-la vers DataSource1
|
|
pour que ADO fonctionne, il faut aussi s'occuper de l'initialisation des
objets COM:
- sélectionnez SERVERCONTROLLER.PAS
- dans l'Inspecteur, initialisez IwServerController.ComInitialization à
ciNormal ou ciMultiThreaded
|
|
finalement, pour permettre la modification d'une ligne,
- ajoutez un composant tAdoCommand à la tForm, et initialisez
Connection vers AdoConnection1. Et il faut basculer ParamCheck sur
False
- vous pouvez aussi utiliser un tAdoDataset, qu'il faut également
initialiser vers AdoConnection1
|
|
à titre de vérification, lancez l'application et vérifiez que vous voyez
bien la table
|
Notez que:
- il faut basculler ComInitialization. Si cette étape n'est pas effectuée,
vous aurez une erreur "coinitialize" qui est, pour les utilisateurs d'ADO
avec IntraWeb, l'erreur la plus fréquente. Cette propriété n'est pas
initialisée par IntraWeb par défaut, car elle n'a lieu d'être que pour ADO
- nous avons aussi montré ici que nous pouvons placer le tDataSource sur la
TIWForm1 (comme nous pouvons choisir de le placer sur tIwUserSession)
- si la connexion ADO ne réussit pas, vous pouvez faire l'exercice avec une
Table BDE, InterBase, ou tout autre composant d'accès
Puis limitez le nombre de lignes et posez les tIwButton pour naviguer dans la
grille, comme il a été présenté dans le paragraphe précédent.
Maintenant occupons nous de la modification d'une tIwDbGrid. Apparemment ce
n'est pas prévu de façon automatique. Plusieurs solutions sont possibles
- utiliser un tIwDbGrid uniquement pour l'affichage, et employer un
tIwNavigator pour synchroniser l'affichage entre la tIwDbGrid et les
tIwDbEdits. Ce n'est pas une solution totalement irréaliste: si vous
regardez VOYAGES.SNCF.COM, il n'y a pas de DataGrid, mais des pages avec
des liens clicables, ou des boîtes d'édition pour entrer vos coordonnées et
payement
- utiliser un tIwData, le peupler et synchroniser l'affichage et la mise à
jour avec la Table
- gérer en parallèle des structures qui mémorisent où nous sommes et lancent
les UPDATE en fonction de cette position
C'est cette dernière solution que nous avons retenue. Elle s'inspire en fait de
ASP.NET, ou, lorsque nous souhaitons modifier une grille. Démontrons d'abord
schématiquement ce que nous souhaitons faire:
- prenons une grille contenant 3 colonnes utiles:
- nous lui adjoignons 3 colonnes pour gérer la modification.
- en mode présentation, la grille comporte une colonne de boutons permettant
de mettre une ligne en mode modification:
- lorsque l'utilisateur a sélectionné une ligne en cliquant sur le bouton de
cette ligne, une nouvelle grille lui est présenté comportant cette fois-ci
- des Edit qui permettent de modifier le contenu d'un champ
- deux boutons permettant de confirmer ou annuler
Voici notre grille en mode modification
An niveau IntraWeb:
Voici donc notre version
|
sélectionnez IwDbGrid1, sélectionnez sa propriété Columns et cliquez sur
l'ellipse ...>
|
|
un Editeur de colonnes est affiché
|
|
cliquez sur l'icône d'ajout (ou "clic droit | add") et ceci 5 fois (pour la
colonne de modification, 2 colonnes de données et 1 colonne pour Post, une
pour Cancel)
|
|
affichez le "Code" et déclarez dans la partie PUBLIC de tIwForm1 les
boutons et les edits qui seront nécessaires, ainsi que les données de suivi
de la ligne sélectionnée:
type TIWForm1=
class(TIWAppForm)
DataSource1: TDataSource;
IWDBGrid1: TIWDBGrid;
next1: TIWButton;
previous1: TIWButton;
// -- ...ooo...
public
m_c_do_modify_button_list: array of tIwButton;
m_c_do_post_button, m_c_do_cancel_button: tIwButton;
m_c_edit_list: array of tIwEdit;
m_do_modify: Boolean;
m_selected_row: Integer;
m_selected_key: String;
procedure handle_do_modify_click(Sender: tObject);
procedure handle_do_cancel_click(Sender: TObject);
procedure handle_do_post_click(Sender: TObject);
end; // TIWAppForm
|
et:
- m_c_do_modify_button_list: la liste verticale des boutons pour
sélectionner une ligne
- m_c_do_post_button, m_c_do_cancel_button: les boutons qui exécutent
Post ou Cancel
- m_c_edit_list: la liste horizontale des tIwEdit permettant de modifier
les champs de la ligne sélectionnée
- m_do_modify: le booléen qui gère l'état affichage / modification de la
grille
- m_selected_row, m_selected_key: les paramètres permettant de
construire la requête
|
|
créez l'événement tIwForm.OnCreate, et créez les do_edit boutons
verticaux, les edits horizontaux et les bouton Post et Cancel:
const k_table_column_count= 2;
procedure TIWForm1.IWAppFormCreate(Sender: TObject);
var l_row_index: Integer;
l_c_do_modify_button: tIwButton;
l_column_index: Integer;
l_c_iwedit: tIwEdit;
begin
SetLength(m_c_do_modify_button_list, IwDbGrid1.RowLimit);
for l_row_index:= 0 to IwDbGrid1.RowLimit- 1 do
begin
l_c_do_modify_button:= tIwButton.Create(Self);
with l_c_do_modify_button do
begin
Caption:= 'Modify_'+ IntToStr(l_row_index);
// -- offset by 1 because of the row header
Tag:= l_row_index+ 1;
OnClick:= handle_do_modify_click;
end; // with l_iwbutton
m_c_do_modify_button_list[l_row_index]:= l_c_do_modify_button;
end; // for l_row_index
SetLength(m_c_edit_list, k_table_column_count);
for l_column_index:= 0 to k_table_column_count- 1 do
begin
l_c_iwedit:= tIwEdit.Create(Self);
with l_c_iwedit do
Text:= 'Edit_'+ IntToStr(l_column_index);
m_c_edit_list[l_column_index]:= l_c_iwedit;
end; // for l_column_index
m_c_do_post_button:= tIwButton.Create(Self);
with m_c_do_post_button do
begin
Color:= clWebLime;
Caption:= 'Post';
OnClick:= handle_do_post_click;
Visible:= False;
end; // with m_c_do_post_button
m_c_do_cancel_button:= tIwButton.Create(Self);
with m_c_do_cancel_button do
begin
Color:= clWebRed;
Caption:= 'Cancel';
OnClick:= handle_do_cancel_click;
Visible:= False;
end; // with m_c_do_cancel_button
end; // IWAppFormCreate
|
Notez que chaque bouton de modification contient dans tIwButton.Tag le
numéro de la ligne. Ceci sera utilisé pour retrouver la ligne courante à
modifier lorsque le bouton sera cliqué.
|
|
sélectionnez la tIwDbGrid, créez son événement OnRenderCell, et affichez,
en fonction de l'état de la dbGrid, soit les boutons pour modifier, soit
les Edit et les boutons pour Poster. Et ces dernier uniquement sur la
ligne courante. En gros, il y a deux états: affichage et modification, et
en fonction du Boolean m_do_modify, nous affichons soit les bouton de
modification, soit les Edits et les boutons d'annulation/post
procedure TIWForm1.IWDBGrid1RenderCell(ACell: TIWGridCell; const ARow,
AColumn: Integer);
procedure draw_in_display_mode;
var l_field_value: String;
begin
if ARow= 0
then // column header
else
case AColumn of
0 : begin
// -- the button column
ACell.Control:= m_c_do_modify_button_list[ARow- 1];
ACell.Control.Enabled:= True;
end;
1 : begin
l_field_value:= UserSession.AdoDataset1.Fields[0].AsString;
ACell.Text:= l_field_value;
end;
2 : begin
l_field_value:=
UserSession.AdoDataset1.FieldByName('Population').AsString;
ACell.Text:= l_field_value;
end;
end; // case AColumn
end; // draw_in_display_mode
procedure draw_in_edit_mode;
procedure display_selected_row;
var l_field_value: String;
begin
case AColumn of
1 : begin
l_field_value:= UserSession.AdoDataset1.Fields[0].AsString;
ACell.Control:= m_c_edit_list[0];
m_c_edit_list[0].Text:= l_field_value;
m_selected_key:= l_field_value;
end;
2 : begin
l_field_value:= UserSession.AdoDataset1.FieldByName('Population').AsString;
ACell.Control:= m_c_edit_list[1];
m_c_edit_list[1].Text:= l_field_value;
end;
3 : begin
ACell.Control:= m_c_do_post_button;
end;
4 : begin
ACell.Control:= m_c_do_cancel_button;
end;
end; // case AColumn
end; // display_selected_row
procedure display_other_row;
var l_field_value: String;
begin
case AColumn of
1 : begin
l_field_value:= UserSession.AdoDataset1.Fields[0].AsString;
ACell.Text:= l_field_value;
end;
2 : begin
l_field_value:=
UserSession.AdoDataset1.FieldByName('Population').AsString;
ACell.Text:= l_field_value;
end;
end; // case AColumn
end; // display_other_row
begin // draw_in_edit_mode
if ARow= 0
then // column header
else
if aColumn= 0
then begin
// -- the modify column
ACell.Control:= m_c_do_modify_button_list[ARow- 1];
ACell.Control.Enabled:= False;
end
else
if ARow= m_selected_row
then display_selected_row
else display_other_row;
end; // draw_in_edit_mode
begin // IWDBGrid1RenderCell
if m_do_modify
then draw_in_edit_mode
else draw_in_display_mode;
end; // IWDBGrid1RenderCell
|
|
|
créez l'événement qui traitera la mise en mode edition:
procedure TIWForm1.handle_do_modify_click(Sender: tObject);
begin
with (Sender as tIwButton) do
begin
m_do_modify:= True;
m_selected_row:= Tag;
m_c_do_post_button.Visible:= True;
m_c_do_cancel_button.Visible:= True;
end; // with Sender as tIwButton
end; // handle_do_modify_click
|
et les événements qui traitent Cancel:
procedure TIWForm1.handle_do_cancel_click(Sender: TObject);
begin
// -- simply revert to display mode
m_do_modify:= False;
// -- reset the browse mode
m_c_do_post_button.Visible:= False;
m_c_do_cancel_button.Visible:= False;
end; // handle_do_cancel_click
|
et finalement la mise à jour (à modifier si vous n'utilisez pas ADO):
procedure TIWForm1.handle_do_post_click(Sender: TObject);
var l_population: String;
begin
clear_display;
display('post '+ m_selected_key);
// -- use the data access controls to update the Table
l_population:= m_c_edit_list[1].Text;
with UserSession.AdoCommand1 do
begin
CommandText:=
'UPDATE country '
+ ' SET population= '+ l_population
+ ' WHERE Name= '''+ m_selected_key+ '''';
// display('cmd '+ CommandText);
Execute;
end; // with UserSession.AdoCommand1
(*
with UserSession.AdoDataset2 do
begin
Open;
if Locate('Name', m_selected_key, [])
then begin
Edit;
FieldByName('Population').AsString:= l_population;
Post;
end;
Close;
end; // with UserSession.AdoDataset2
*)
// -- refresh AdoDataset1
UserSession.AdoDataset1.Close;
UserSession.AdoDataset1.Open;
// -- reset the browse mode
m_do_modify:= False;
m_c_do_post_button.Visible:= False;
m_c_do_cancel_button.Visible:= False;
end; // handle_do_post_click
|
Nous avons laissé en commentaire la modification via AdoDataset2
|
|
compilez et exécutez
|
|
voici la grille en mode affichage:
|
|
cliquez sur le second tButton:
|
|
voici la grille en mode modification:

|
Notez
- tout d'abord nous nous sommes entêtés à vouloir utiliser ADO. Lorsque nous
avons essayé de mettre à jour, nous avons eu une exception "OleException,
missing required parameter". Après quelques heures d'essai, nous avons
décidé d'utiliser un tAdoDataSet, et Locate n'a pas fonctionné, car la
colonne COUNTRY n'existait pas. Nous avions confondu EMPLOYEE_GDB.COUNTRY et
DBDEMOS_MDB.COUNTRY, et le nom correct pour la colonne contenant le nom du
pays est ici NAME. "OleException, missing parameter" était naturellement le
message le plus approprié pour nous mettre sur la voie. Pas aussi inutile
que "Syntax Error", mais pas loin, voire pire ...
- revenons à notre sujet. Nous aurions pu, comme ASP.NET utiliser la même
colonne pour les boutons de modification et ceux de confirmation ou
annulation. Nous avons tout laissé pour mieux montrer ce qui se passait
- nous avons créé des listes de boutons. Il faudrait naturellement les
libérer, et ceci dans tIwForm.OnDestroy
- nous avons utilisé une grille de taille fixe pour pouvoir plus facilement
allouer nos tables de boutons. Il serait possible de parcourir la Table pour
allouer un nombre variable en fonction de la taille de la table
- en plus des dbGrid, il existe aussi une grille tIwGrid (non liée à une
base de données) que nous pouvons remplir, modifier etc
- il arrive que lorsque nous créons des tIwButton ou autre contrôle par code,
nous ne connaissions pas l'UNITé Intraweb à importer. Nous pouvons
naturellement recherche cette unité en utilisant l'aide. Une solution bien
connue est de déposer un exemplaire de ce type de contrôle via la Palette,
de compiler (ce qui force Delphi à ajouter le bon USES, puis à retirer ce
composant
- SELF est impératif dans tIwButton.Create(SELF) (sinon le clic ne sera
pas pris en compte)
- peut-être les nouvelles versions d'Intraweb faciliteront la modification
d'une grille, mais les messages trouvés sur les forums n'en parlent pas
- la stratégie utilisée pour modifier une grille est similaire à celle que
nous avons utilisée pour présenter la navigation dans une grille en
utilisant CGI, et est la même que celle utilisée, nous l'avons déjà indiqué,
par ASP.NET
- nous pourrions utiliser la même technique pour nous positionner sur une
ligne en utilisant la grille
- il faudrait aussi gérer l'inhibition de previous et next pendant la
modification
- la dernière image ci-dessus présente aussi un tIwMemo. Naturellement, nous
n'avons pu nous empêcher d'afficher différents message de mise au point,
comme sous Windows. Ici le memo affiche la requête qui va être exécutée.
Les méthodes pour afficher et effacer le memo sont dans les sources du .ZIP
- en ce qui concerne la tIwDbGrid:
- UseFrame permet de supprimer le titre de la tIwDbGrid. UseSize affiche
un cadre autour de la grille
- tIwDbGrid.Colums[0] n'est pas accepté. Il faut utiliser
tIwDbGrid.Colums[0].Items[0]
- apparemment, le titre des colonnes est compté comme une ligne de la
tIwDbGrid
- les tIwDbGridColumn ont aussi un événement OnClick que nous aurions
peut être pu utiliser au lieu de mettre en place des boutons Modify
- CellPadding est le gras dans une cellule, CellSpacing la taille de la
bordure (comme pour les <TABLE>s .HTML, naturellement
- BgColor et la couleur de la zone dessinée
- Options.dgShowTitles permet de supprimer les titres des colonnes
4.5 - Gestion des Sessions
Par définition, le protocole .HTTP est sans état: lorsqu'un Client demande une
page, le Serveur la lui envoie, et oublie tout de cette requête. Ceci évite
d'avoir à gérer au niveau du Serveur l'état de chaque Client.
Certaines applications nécessitent toutefois une gestion de la session. Si nous
souhaitons afficher les lignes d'une Table par groupe de 10 lignes, il faut
bien mémoriser quelque part que nous avons affiché les lignes 09, et lorsque
nous cliquons "Next", nous souhaitons les lignes 1011. Il faut donc que
notre application (et non pas le Serveur) se charge de gérer l'état du
dialogue entre le Serveur et |