IX. Manipulation des composants▲
Votre connaissance du langage Pascal augmentant, il est désormais possible de nous
attaquer à un morceau de choix : l'utilisation des composants. Par composant, on
entend ici en fait les fiches, qui sont (techniquement seulement) des composants,
ainsi que tous les composants que vous pourrez être amenés à manipuler.
Ce chapitre sera encore un peu théorique et approfondira des notions déjà survolées
auparavant. Vous apprendrez ainsi plus en détail ce que sont les composants, comme on
les utilise, comment on accède à leurs propriétés, ce que sont les événements et
comment on les utilise. Quelques exemples tenteront de rompre la monotonie de ce
genre de cours. Un mini-projet est proposé vers la fin du chapître.
IX-A. Introduction▲
Nous avons déjà parlé dés le début du guide de l'interface de vos applications.
Ces interfaces seront constituées d'un ensemble bien structuré d'éléments pour la
plupart visibles : boutons, cases à cocher, textes, menus... mais aussi d'autres
non visibles sur lesquels nous reviendront. Chacun de ces éléments individuels se
nomme « composant ». Un composant est en fait une sorte de boite noire qui a
plusieurs têtes. La partie la plus facile à saisir d'un composant est assurément
son coté visuel, mais ce n'est pas le seul aspect d'un composant : il contient
tout un tas d'autres choses telles que des propriétés, des événements et des
méthodes. Les propriétés sont des données de types très divers, dont certaines
sont des constantes et d'autres des variables. Les événements sont tout autre :
ils permettent d'exécuter une procédure dans certaines circonstances en réponse à
un evènement concernant le composant (ces circonstances dépendent de
l'evènement), comme par exemple lors du clic sur un bouton (vous devez
commencer à vous familiariser avec celui-là, non ?). Les méthodes sont tout simplement
des procédures et des fonctions internes au composant.
Au niveau du code source, chaque composant est une variable. Cette variable est de
type objet. Vous n'avez nul besoin pour l'instant de savoir ce qu'est et comment
fonctionne un objet, car ceci fera l'objet d'un long chapitre. Ce qu'il est
intéressant pour vous de savoir pour l'instant, c'est qu'un objet ressemble beaucoup
(en fait, pas du tout, mais on fera semblant de le croire, hein ?) à un
enregistrement (type record) amélioré. Un objet peut contenir des données,
comme les record, mais aussi des procédures, des fonctions, et encore
certaines autres choses spécifiques aux objets dont nous parlerons plus tard et qui
permettent entre auters de faire fonctionner les événements.
Au risque d'avoir l'air de changer de sujet (ce qui n'est pas du tout le cas),
avez-vous parfois regardé cette partie du code source de l'unité 'Principale.pas'
(fiche vierge créée avec une nouvelle application) située dans l'interface ? Jetez-y
un coup d'oeil, vous devriez voir quelque chose du genre :
type
TForm1 = class
(TForm)
private
{ Private declarations }
public
{ Public declarations }
end
;
var
Form1: TForm1;
Cet extrait de code, malgré son apparente complexité, fait bien peu de choses : il
déclare un nouveau type (bloc type) nommé 'TForm1' et une variable nommée
'Form1' qui est justement de type 'TForm1'. La déclaration du type 'TForm1' devrait
vous rappeler celle d'un type enregistrement (record), mis à part qu'ici on
utilise le mot class suivi d'autre chose entre parenthèses. Une autre
différence est que des mots réservés (private (« privé » en anglais) et
public) sont présents à l'intérieur de cette déclaration, ne vous en occupez
surtout pas pour l'instant.
La variable nommée 'Form1' est la fiche. Comprenez par là que cette variable
contient tout ce qui a trait à la fiche : sa partie visible que vous pouvez voir en
lancant l'application aussi bien que sa partie invisible, à savoir ses propriétés et
ses événements. Le type de cette variable 'Form1' est 'TForm1'. Ce type est défini
dans l'unité car il est spécifiquement créé pour cette fiche. Tout ajout de
composant à la fiche viendra modifier ce type et donc modifiera la variable 'Form1'
(mais pas sa déclaration).
Essayez par exemple de poser un bouton sur une fiche vierge. L'extrait de code
devient alors :
type
TForm1 = class
(TForm)
Button1: TButton;
private
{ Private declarations }
public
{ Public declarations }
end
;
var
Form1: TForm1;
Le seul changement par rapport au premier code source est la ligne :
Button1: TButton;
Cette ligne anodine déclare en fait une variable « interne » au type 'TForm1' (au même sens que dans un enregistrement). La variable est nommée 'Button1' et est de type 'TButton'. Cette variable est le bouton, c'est-à-dire bien plus que ce que vous voyez à l'écran quand vous lancez l'application. Tout comme la variable 'Form1', la variable 'Button1' contient bien d'autres choses que sa partie visible : ce qui fait fonctionner les composants.
IX-B. Aperçu de la structure interne d'un composant▲
Il est ici hors de question de rentrer déjà dans les détails de la structure d'un
composant, c'est quelque chose que nous verrons plus tard. Il est cependant
intéressant pour vous de connaître au moins la constitution théorique d'un composant
: c'est quelque chose qui vous aidera mieux, j'en suis certain, à maîtriser ces «
boites noires ».
Voici un schémas qui donne une représentation d'un composant :
Vous pouvez voir sur ce schémas qu'un composant a plusieurs aspects regroupés autour d'un coeur central.
- Un premier aspect, souvent le plus connu, est l'interface (le coté visible) du composant. C'est ainsi que lorsque vous placez un composant 'Button' sur une fiche, vous voyez un bouton apparaître. Ce bouton n'est après tout qu'une image qui est la partie visible d'un très très gros iceberg.
- D'un autre coté, on voit les propriétés d'un composant. Ces éléments, dont nous avons déjà un peu parlé sont pour une partie présentes dans l'inspecteur d'objets lorsque le composant est sélectionné, et pour l'autre partie utilisables depuis le code source uniquement (nous verrons comment et pourquoi en temps et en heure).
- Un troisième aspect d'un composant est ses événements. Ces événements donnent simplement la possibilité (et non l'obligation comme certains le croient au début) de réagir en réponse à une intervention sur le composant : clic, double clic, mouvement de souris, appui d'une touche, activation, etc... autant de procédures qui pourront être créées et reliées automatiquement au composant par Delphi. Ces procédures seront exécutées à chaque fois que l'evènement se produira.
- Le dernier aspect d'un composant est constitué par ses méthodes. On emploie le terme de méthode pour désigner les procédures et fonctions internes à un composant (et plus généralement à tout objet). Chacune de ces méthodes a un rôle à jouer dans le composant, et dans lui seul. Les méthodes sont les petits bras articulés qui vont faire le travail à l'intérieur du composant.
Afin d'illustrer tout cela, je vais reprendre un exemple trouvé dans l'aide en ligne d'un autre langage (Visual Basic pour ne pas le citer...) : celui d'un ballon. Notre ballon est un objet, et dans notre cas un composant (c'est plus qu'un simple objet). Reprenons maintenant les 4 aspects d'un composant en pensant à notre ballon.
- L'interface, ici, sera évidemment un morceau de caoutchouc. Mais n'importe quel bout de caoutchouc n'est pas un ballon, il a une forme, une composition, certaines particularités physiques qui font un ballon d'un morceau de caoutchouc. Nous allons pouvoir modifier ce ballon « virtuel » à volonté.
- C'est ici qu'interviennent les propriétés : couleur du ballon, contenu du ballon (air, hélium, eau, ...), gonflé ou dégonflé, etc... Ces caractéristiques vont pouvoir être règlées pour nous permettre d'avoir le ballon dont nous aurons besoin : ballon rouge gonflé à l'hélium par exemple.
- Comme evènement, je prendrai la rencontre fortuite de notre ballon et d'une épingle : c'est un evènement. Dans la réalité, notre ballon n'aurait pas d'autre choix que d'éclater : se dégonfler en émettant un bruit d'explosion. Eh bien, pour notre ballon virtuel, c'est la même chose : il va nous falloir changer l'état de notre ballon (changer la valeur de la propriété « gonflé » et emettre un son). Prenons un exemple moins barbare : la personne qui tient le ballon le lâche. Plusieurs possibilités sont à envisager alors : si le ballon est gonflé d'hélium, il va s'envoler, si il est vide, il va tomber. Nous devrons gèrer plusieurs scénarios et donc faire une analyse de l'état de notre ballon avant de le modifier.
- Les méthodes, enfin, sont tout ce qui va nous permettre de modifier notre ballon : le faire monter, le faire tomber, lui faire emettre un bruit d'explosion, se dégonfler, se gonfler (même si en réalité ce n'est pas physiquement très correct, personne ne sera présent dans votre programme pour gonfler les ballons !).
IX-C. Manipulation des propriétés d'un composant▲
Cette partie vous explique les moyens de modifier les propriétés d'un composant. Ces moyens sont au nombre de deux. Delphi, tout d'abord, vous propose de faire ces modifications de façon visuelle. Le code Pascal Objet, d'autre part, vous permet de faire tout ce que fait Delphi, et bien d'autres choses.
IX-C-1. Utilisation de Delphi▲
Delphi est un fantastique outil de programmation visuelle, vous ais-je dit au début
de ce guide. C'est le moment d'expliquer comment tirer partie de cela : Delphi vous
propose divers outils pour modifier facilement les propriétés des composants.
Lorsque vous avez une fiche sous Delphi, vous pouvez la redimensionner et la
déplacer à volonté : vous manipulez sans le savoir les quatre propriétés nommées
'Height', 'Width', 'Top' et 'Left' de cette fiche. Ces propriétés, vous pouvez
également les modifier depuis l'inspecteur d'objets, qui a l'avantage de présenter
d'autres propriétés non accessibles ailleurs. C'est la même chose lorsque vous
placez un composant sur une fiche : vous pouvez le dimensionner à volonté sur la
fiche comme dans un logiciel de dessin, mais vous pouvez aussi utiliser
l'inspecteur d'objets pour modifier les mêmes propriétés que pour la fiche.
Pour déplacer ou dimensionner une fiche, il vous suffit de le faire comme n'importe
quelle autre fenêtre sous Windows. Pour dimensionner un composant, il faut le
sélectionner d'un simple clic, puis déplacer les poignées (petits carrés noirs tout
autour du composant) avec la souris. Pour déplacer un composant, laissez faire
votre intuition : prenez le composant à la souris en cliquant une fois dessus et en
maintenant le bouton gauche de la souris, puis déplacez-le. Pour le poser, relâchez
simplement le bouton de la souris. Un autre moyen consiste à sélectionner le
composant, puis à maintenir enfoncée la touche 'Ctrl' du clavier tout en utilisant
les fléches de direction, ce qui déplace la sélection d'un pixel dans l'une des 4
directions désignée.
Au niveau de la sélection, vous pouvez sélectionner plusieurs composants en en
sélectionnant un premier, puis en sélectionnant les autres en maintenant la touche
'Shift' ('Maj' en français) du clavier. Vous pouvez également dessiner un rectangle
avec la souris en cliquant et en maintenant enfoncé le bouton de la souris à un
emplacement sans composant, puis en allant relâcher le bouton à l'angle opposé du
rectangle : tous les composants ayant une partie dans ce rectangle seront
sélectionnés. Pour annuler une sélection, appuyez sur 'Echap' jusqu'à ce que toutes
les poignées de sélection aient disparu.
En ce qui concerne les tailles et positions des éléments, vous disposez également
sous Delphi d'une palette d'alignements prédéfinis accessible par le menu « Voir »,
choix « Palette d'alignement ». Cette palette permet, en sélectionnant par exemple
plusieurs boutons, de répartir également l'espace entre ces boutons, de centrer le
groupe de boutons, et bien d'autres choses que vous essaierez par vous-même.
Pour les autres propriétés, il est nécessaire d'utiliser l'inspecteur d'objets.
Vous pouvez directement modifier une propriété commune à plusieurs composants en
les sélectionnant au préalable : l'inspecteur d'objets n'affichera alors que les
propriétés communes à tous les composants sélectionnés.
Une propriété des plus importantes dont vous devez maintenant apprendre l'existence
et le fonctionnement est la propriété 'Name' ('Nom' en français). Cette propriété
est une propriété spéciale utilisable uniquement sous Delphi et qui décide du nom
de la variable qui stockera le composant. Prenons par exemple la seule fiche d'un
projet vide : sa propriété 'Name' devrait être « Form1 » (voir capture d'écran
ci-dessous).
Vous vous souvenez certainement de l'extrait de code dont nous avons parlé dans l'introduction, cet exemple où était déclarée une variable 'Form1'. C'est la propriété 'Name' qui décide du nom de cette variable. Changez donc ce nom en un nom plus adéquat, tel « fmPrinc » ('fm' comme 'Form': habituez-vous dés à présent à rencontrer beaucoup de ces abréviations en deux lettres minuscules désigant le type de composant et donc différentes pour chaque type de composant. je vous conseille d'utiliser ces abréviations dés que vous les connaissez, ou de créer les vôtres et de vous y tenir absolument). Examinez ensuite le code source, le voici tel qu'il devrait être :
type
TfmPrinc = class
(TForm)
private
{ Private declarations }
public
{ Public declarations }
end
;
var
fmPrinc: TfmPrinc;
Vous voyez que le nom de la variable à changé, mais aussi que le type utilisé a
aussi changé. Le nom de ce type est en fait généré en fonction du nom que vous
indiquez pour la propriété 'Name' : un T majuscule est simplement ajouté devant
pour signifier que c'est un Type. habituez-vous à rencontrer ce T majuscule dans
beaucoup de situations, et même si possible à l'utiliser vous-mêmes comme je le
fais dans le guide et en dehors.
Placez maintenant trois boutons sur la fiche. L'extrait de code devrait maintenant
être :
type
TfmPrinc = class
(TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
private
{ Private declarations }
public
{ Public declarations }
end
;
var
fmPrinc: TfmPrinc;
Vous avez certainement remarqué que trois lignes ont fait leur apparition, déclarant chacune une variable « interne » au type 'TfmPrinc'. Chacune de ces variables est un des boutons que vous venez de placer sur la fiche. Le type employé pour chacune des trois variables est 'TButton', qui est le type de composant utilisé pour les boutons. Renommez ces trois boutons (toujours depuis l'inspecteur d'objets, jamais depuis le code source !) en les nommant à votre convenance, et en utilisant comme abréviation de deux lettres : 'bt'. Voici le code source qui vous indique les noms que j'ai personnellement utilisés.
type
TfmPrinc = class
(TForm)
btBonjour: TButton;
btSalut: TButton;
btCoucou: TButton;
private
{ Private declarations }
public
{ Public declarations }
end
;
var
fmPrinc: TfmPrinc;
Il n'est pas encore très important pour vous de connaître cette partie du code source, mais il est tout de même essentiel de savoir que cette propriété 'Name' désigne un nom de variable et doit donc contenir un identificateur valide, respectant la règle du préfixe de deux minuscules désignant le type de composant. Nous allons encore creuser un petit peu le sujet en créant une procédure répondant à un évènement (comme vous l'avez certainement déjà fait plusieurs fois) : double-cliquez sur l'un des trois boutons sous Delphi, une procédure est alors générée. N'écrivez rien dans cette procédure, ce n'est pas l'objet ici. Retournez plutôt voir le morceau de code déclarant le type 'TfmPrinc'. Vous devez voir une nouvelle ligne.
type
TfmPrinc = class
(TForm)
btBonjour: TButton;
btSalut: TButton;
btCoucou: TButton;
procedure
btBonjourClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end
;
var
fmPrinc: TfmPrinc;
Cette ligne déclare la procédure nommée 'TfmPrinc.btBonjourClick', mais en ne
mettant qu'une partie du nom : 'btBonjourClick'. C'est en fait le vrai nom de la
procédure. Jusqu'ici, je vous ai laissé croire (mais qui viendra s'en plaindre ?)
que 'TForm1.btBonjourClick' était le nom de la procédure, ce qui est impossible
puisque ce n'est pas un identificateur. 'btBonjourClick', en revanche, est un
identificateur valide. Quid alors de 'TfmPrinc.' ? La réponse est assez simple :
c'est le nom « qualifié » de la procédure. Ce nom comporte le nom du type auquel il
appartient : 'TfmPrinc', suivi d'un point (.) pour indiquer qu'on va maintenant
désigner un élément intérieur à ce type, à savoir ici la procédure
nommée 'btBonjourClick'. C'est pour cela que la déclaration à l'intérieur du type
'TfmPrinc' ne comporte pas ce préfixe ('TfmPrinc.') : on est en fait déjà à
l'intérieur du type !
Un autre fait intéressant qu'il vous faut connaître est que dans cette partie du
code source que nous venons d'étudier succintement, tous les composants, ainsi que
toutes les procédures associées à des événements seront déclarées automatiquement
par Delphi. La gestion de cette partie du code source incombe exclusivement à
Delphi, vous n'aurez donc pas le droit d'y toucher (sous peine de faire des bétises
parfois irréparables). Cela ne vous épargne pas de connaître l'existence et de
comprendre ce qui s'y trouve. Nous aurons au cours du guide l'occasion de revenir
sur cette partie.
Pour revenir sur un terrain plus stable, de nombreuses autres propriétés sont
disponibles dans l'inspecteur d'objets. Leur nombre et leur diversité peut
effrayer, mais c'est un gage de puissance et de flexibilité des composants. Pour
l'instant, nous n'allons pas manipuler ces propriétés, puisque vous l'avez
normalement déjà fait au fur et à mesure des autres chapitres du guide, mais si le
coeur vous en dit, n'hésitez pas à vous lancer et à vous tromper : en cas d'erreur,
fermez le projet sans enregistrer, puis créez-en un nouveau !
IX-C-2. Utilisation du code source▲
L'utilisation de l'inspecteur d'objets est certes intuitive, facile et sympathique, mais d'une efficacité très limitée. Voici les limites principales :
- Certaines propriétés sont inaccessibles depuis l'inspecteur d'objets.
- On ne peut pas modifier les propriétés pendant l'exécution de l'application.
- On ne peut manipuler que certains composants (nous verrons cela bien plus tard : certains composants, du fait de leur absence dans la palette des composants, ne peuvent être manipulés que depuis le code source de l'application).
De plus, il ne faut pas oublier, mais cela, vous ne le savez peut-être pas encore
si vous êtes novice en programmation, que l'inspecteur d'objets est une facilité
offerte aux programmeurs, mais qu'avant l'existence de Delphi, il fallait bien
faire tout ce que fait maintenant l'inspecteur d'objet, et le faire depuis le code
source : c'était parfois pénible, souvent fastidieux et source d'erreurs.
Il paraît donc inconcevable de se limiter à l'inspecteur d'objets pour la
manipulation des propriétés : c'est très souvent dans le code Pascal d'une
application que s'effectueront le plus grand nombre de manipulations de
propriétés.
Une propriété se manipule, selon les cas, comme une variable ou une constante. En
fait, une propriété est un élément spécial dont vous n'avez pour l'instant pas à
connaître les détails, mais qui permet la lecture des données (on peut lire la
valeur de la propriété, l'écriture de ces données (on peut modifier la valeur de
cette propriété) ou les deux à la fois. La presque totalité des propriétés est soit
en lecture seule soit en lecture/écriture.
- Les propriétés en écriture seule sont rarissimes, vous n'en rencontrerez probablement jamais, l'intérêt de ce genre de propriété reste à prouver.
- Les propriétés en lecture seule sont fréquentes : elles permettent de se renseigner sur l'état d'un composant, sans pouvoir agir sur cet état (Dans l'aide de Delphi, ces propriétés sont souvent signalées par une petite flèche bleue). Ces propriétés se manipulent comme n'importe quelle constante.
- Les propriétés en lecture/écriture sont les plus courantes : elles permettent tout autant de s'informer que de modifier l'état du composant en modifiant la valeur de la propriété.
Pour utiliser une propriété d'un composant dans du code Pascal, il faut séparer deux cas, selon qu'on est ou pas dans une procédure interne à la fiche qui contient le composant.
-
Si on est dans une procédure interne à la fiche qui contient ce composant (le composant et la procédure dans laquelle on souhaite manipuler une propriété de ce composant doivent être déclarés dans le même type), ce qui est le cas le plus fréquent, il faut écrire
- le nom du composant auquel elle appartient (le nom de la variable qui stocke ce composant : cette variable, dans notre cas, est interne au type qui est utilisé pour déclarer la variable qui stocke la fiche)
- un point (.)
- le nom de la propriété
Ainsi, pour désigner la propriété 'Caption' d'un bouton nommé (la valeur de la propriété 'name' est le nom du composant) 'btBonjour', on devra écrire :
SélectionnezbtBonjour.Caption
On pourra utiliser cette expression comme une variable puisque la propriété 'Caption' est en lecture/écriture pour les boutons (composants de type 'TButton').
-
Dans les autres cas, c'est-à-dire dans les procédures non internes à la fiche, mais présentes dans la même unité, ou dans les procédures d'autres unités, il faudra en écrire un peu plus. C'est ici une notion des plus importantes : depuis l' « intérieur » de la fiche (c'est-à-dire dans une procédure interne à cette fiche), on peut utiliser tout ce que contient la fiche. Lorsqu'on est à l'extérieur de la fiche, il va d'abord falloir y entrer, puis ce sera le même procédé que ci-dessus. Voici ce qu'il faudra écrire :
- le nom de la fiche, c'est à dire le nom de la variable qui contient la fiche (c'est également le contenu de la propriété 'Name' de la fiche)
- un point (.)
- le nom du composant auquel elle appartient (le nom de la variable qui stocke ce composant : cette variable, dans notre cas, est interne au type qui est utilisé pour déclarer la variable qui stocke la fiche)
- un point (.)
- le nom de la propriété
Ainsi, depuis une autre fiche par exemple, pour désigner la propriété 'Caption' d'un bouton nommé 'btBonjour' d'une fiche nommée 'fmPrinc', on devra écrire :
SélectionnezfmPrinc.btBonjour.Caption
Pour résumer ces divers cas qui peuvent paraître compliqués au premier abord, il
faut simplement s'assurer, lorsqu'on veut utiliser un élément, que ce soit un
composant ou une propriété, qu'on y a accès directement, c'est-à-dire sans accèder
à une (autre) fiche. Beaucoup de choses dans le style de programmation employé sous
Delphi sont conçues sur ce principe : pour accèder à une information écrite sur un
papier, on doit d'abord trouver ce papier en ouvrant le classeur qui le contient,
et aussi parfois trouver le classeur dans une armoire, trouver le bureau qui
contient l'armoire... ces dernières étapes étant bien entendues inutiles si on a
déjà le classeur ouvert devant soi lorsqu'on a besoin de l'information sur la
feuille de papier !
Avant de passer aux incontournables exemples (je sens que certains d'entre vous en
auront absolument besoin), je tiens à revenir sur ce point qui apparaît pour
séparer les noms de fiches, de composants et de propriétés. Le point, en Pascal
Objet, est un opérateur qui permet d'accèder au contenu de ce qui le précède :
lorsqu'on écrit 'fmPrinc.', on va s'adresser ensuite à quelque chose d'interne à
fmPrinc. De même, lorsqu'on écrit 'btBonjour.', on s'adresse ensuite à quelque
chose d'interne au bouton. Lorsqu'on est à l'extérieur de la fiche 'fmPrinc', on
doit écrire 'fmPrinc.btBonjour.' pour s'adresser à quelque chose d'interne au
bouton. Pour accèder à la propriété 'Caption' de la fiche, il suffira d'écrire
alors 'fmPrinc.Caption'. Lorsqu'on sera en plus à l' « intérieur » de cette fiche,
'Caption' désignera directement la propriété 'Caption' de la fiche.
Pour revenir à notre exemple de la feuille de papier et du classeur, imaginons tout
d'abord que vous ayez le classeur devant vous : vous indiquerez «
page824.information75 » pour accèder à l'information sur la page (en admettant
évidemment que vous connaissiez le nom du papier et le nom de l'information, ce qui
revient à savoir quelle information vous cherchez et sur quel papier vous comptez
la trouver. Imaginons maintenant que vous soyez dans le mauvais bureau : vous
devrez alors écrire tout cela : «
mon_bureau.armoire_archives.classeur_clients.page824.information75 ». C'est, je
vous l'accorde, parfois un peu pénible, mais c'est la clé d'une bonne organisation
: vous saurez toujours comment trouver l'information désirée sous Delphi à partir
du moment où vous saurez où elle est.
Venons-en à un exemple très pratique : créez un projet vierge et placez deux
boutons sur la feuille principale. Nommez la feuille « fmPrinc » et les deux
boutons : « btTest1 » et « btTest2 » (utilisez la propriété 'name' en sélectionnant
la fiche, puis chaque bouton). Donnez une tête présentable à tout cela en modifiant
également les propriété 'Caption', 'Width', 'Height', 'Top' et 'Left' des boutons
et de la fiche (selon votre humeur du moment). Double-cliquez sur le bouton
'btTest1', ce qui va générer une procédure locale à la fiche 'fmPrinc', nommée
'btTest1Click' (rien de très nouveau jusqu'à maintenant, mis à part que vous pouvez
certainement remarquer que le nom de cette procédure ne doit rien au hasard,
puisqu'il est composé du nom du composant et de 'Click'). Faites de même pour le
second bouton ('btTest2').
Nous allons modifier les propriétés 'Caption' des deux boutons et de la fiche
depuis ces procédures, ce qui signifie qu'un clic sur l'un des boutons changera les
textes affichés sur les boutons ou dans le titre de la fenêtre. Utilisez l'extrait
de code ci-dessous pour complèter vos procédures :
procedure
TfmPrinc.btTest1Click(Sender: TObject);
begin
Caption := 'Titre de fenêtre'
;
end
;
procedure
TfmPrinc.btTest2Click(Sender: TObject);
begin
btTest1.Caption := 'Titre de bouton 1'
;
btTest2.Caption := 'Titre de bouton 2'
;
end
;
Exécutez l'application et cliquez sur chacun des boutons en prettant attention à
l'effet produit : un clic sur l'un des boutons change le titre de la fenêtre, ce
qui correspond à la propriété 'Caption' de la fiche. Un clic sur l'autre bouton, en
revanche, modifie le texte affiché sur chacun de ces deux boutons : leur propriété
'Caption' est modifiée. Notez que cliquer plusieurs fois sur les boutons n'a pas
d'effet visible puisque les valeurs des propriétés ne changent plus. Malgré cela,
le code des procédures est bien exécuté.
Revenons maintenant à l'extrait de code ci-dessus : la ligne :
Caption := 'Titre de fenêtre'
;
modifie la propriété 'Caption' de la fiche. Comme la procédure fait partie de la fiche (deux moyens de s'en assurer : la procédure est déclarée à l'intérieur du type 'TfmPrinc' plus haut dans le code, et le nom de la procédure est précédé de 'TfmPrinc.'), nous n'avons rien à faire de plus pour accèder à l'intérieur de la fiche : on utilise directement 'Caption', et ceci comme une variable, à laquelle on affecte une chaîne de caractères. L'exécution de cette petite ligne va suffir pour changer le titre de la fenêtre en ce que nous voudrons. Le code de la deuxième procédure, comme vous l'avez vu en exécutant l'application, modifie le texte des deux boutons. Pour cela, on a utilisé deux instructions similaires, une pour chaque bouton. Pour modifier la propriété 'Caption' du bouton 'btTest1', on doit d'abord s'adresser au composant 'btTest1', placé sur la fiche. On est sur la fiche, donc on s'adresse directement au composant, puis à la propriété :
btTest1.Caption := 'Titre de bouton 1'
;
Passons maintenant à quelque chose de plus intéressant. Créez un nouveau projet, puis placez sur la seule fiche deux composants : un composant Edit (zone d'édition, l'icône est ) et un bouton classique. Placez-les pour obtenir quelque chose du style :
Modifiez les propriétés Caption de la fiche et du bouton en mettant respectivement
« Perroquet » et « Répeter ». Nommez la fiche 'fmPrinc', le bouton 'btRepeter' et
la zone d'édition (on l'appelera désormais "l'Edit" pour faire plus court)
'edMessage'. La propriété 'Text' (de type string) d'un composant Edit (type TEdit)
permet de lire et écrire le texte contenu (mais pas forcément entièrement affiché,
faute de manque de place à l'écran) dans la zone d'édition. Utilisez l'inspecteur
d'objets pour effacer le texte indésirable qui doit s'y trouver : « edMessage »
(nous expliquerons plus tard pourquoi ce texte a changé quand vous avez modifié la
propriété 'Name').
Votre interface doit maintenant être quelque chose du style :
Evitez maintenant de visualiser dans votre navigateur le code source présent
ci-dessous, réalisez l'exercice et regardez ensuite la solution pour éventuellement
vous corriger. Le principe du programme simple que nous allons créer va être de
répeter ce qui est écrit dans la zone d'édition lors d'un clic sur le bouton
'Répeter'. Générez la procédure répondant au clic sur le bouton (double-clic sur
celui-ci). La première instruction va consister à attribuer à une variable chaîne
le texte écrit dans la zone d'édition. Déclarez une variable 'Msg' de type Chaîne.
Tapez maintenant (en tant que première instruction) l'instruction qui affecte à la
variable 'Str' la valeur de la propriété 'Text' de la zone d'édition (nommée
'edMessage'). La deuxième instruction, plus simple consiste à afficher ce message
par un moyen habituel déjà souvent utilisé.
Vous avez terminé ? Voici la solution :
procedure
TfmMain.btRepeterClick(Sender: TObject);
var
Msg: string
;
begin
Msg := edMessage.Text;
ShowMessage(Msg);
end
;
La seule difficulté est dans la première ligne : la propriété 'Text' du composant 'edMessage' s'obtient par 'edMessage.Text', car la procédure est interne à la fiche, et peut donc s'adresser directement aux composants. Notez que l'on aurait pu faire plus court en se passant de la variable Msg et en écrivant l'instruction unique :
ShowMessage(edMessage.Text);
car edMessage.Text est bien de type string et peut donc être transmis en
tant que paramètre à 'ShowMessage'.
Nous avons fait la manipulation dans les deux sens : écriture (exemple avec
'Caption') et lecture (à l'instant). Dans un programme Pascal Objet sous Delphi,
nous manipulerons les propriétés à longueur de temps. Il est donc primordial de
vous entraîner un peu. Voici un autre exemple plus complexe.
Créez un projet vierge, placez un composant CheckBox (Case à cocher, son icône est
), une zone d'édition, et un bouton. Le but de cet
exercice est de contrôler l'état de la case à cocher avec la zone d'édition et le
bouton. Avant cela, il vous faut une propriété de plus : la propriété 'Checked' de
type Booléen des cases à cocher (type TCheckBox) permet de lire ou fixer l'état de
la case (cochée ou non cochée). Lorsque 'Checked' vaut 'true', la case apparaît
cochée, et non cochée lorsque 'Checked' vaut 'false'. En outre, la propriété
'Caption' décide du texte présent à coté de la case. Ceci permet de (dé)cocher la
case en cliquant sur ce texte (fonctionnement classique de Windows, rien de nouveau
ici).
Nommez la case à cocher 'cbTest', le bouton 'btModif' et la zone d'édition
'edEtat', la fiche principale (et unique), comme presque toujours, sera nommée
'fmPrinc'. Donnez des textes acceptables à tout cela pour obtenir l'interface
ci-dessous :
Générez la procédure de réponse au clic sur le bouton. Nous allons réaliser deux
tests similaires : nous comparerons le texte écrit dans la zone d'édition à 'false'
ou 'true', et s'il est égal à l'un des deux, la case à cocher sera mise à jour en
conséquence. En outre, le texte de la zone d'édition sera effacé à chaque clic sur
le bouton pour qu'on puisse y retaper facilement du texte.
Voici le code source :
procedure
TfmPrinc.btModifClick(Sender: TObject);
begin
if
edEtat.Text = 'false'
then
cbTest.Checked := false
;
if
edEtat.Text = 'true'
then
cbTest.Checked := true
;
edEtat.Text := ''
;
end
;
Cet exemple montre bien à quel point on peut assimiler les propriétés à des
variables (mais pas les confondre, attention, ce ne sont PAS des variables).
'edEtat.Text' par exemple est d'abord utilisé deux fois en lecture pour être
comparé à une autre chaîne, puis en écriture en étant fixé à la chaîne vide. La
propriété 'Checked' de 'cbTest' est modifiée comme le serait une variable
booléenne, à ceci près qu'en plus, un effet visuel est produit : la coche apparait
ou disparait.
D'innombrables exemples seront présents dans la suite du guide, puisque la
manipulation des composants est un passage obligé sous Delphi. Pour l'instant
passons à un sujet très lié et non moins intéressant : la manipulation des méthodes
des composants.
IX-D. Manipulation des méthodes d'un composant▲
Il n'est pas ici question, comme pour les propriétés, d'utiliser l'interface de
Delphi : tout ce qui touche aux méthodes touche à l'exécution des applications,
c'est-à-dire que toutes les manipulations s'effectuent depuis le code source.
Nous avons déjà un peu parlé des méthodes, en disant qu'il s'agissait de procédures
ou fonctions internes aux composants. Ces méthodes font tout le travail dans un
composant. Même la manipulation des propriétés cache en fait la manipulation des
méthodes, mais nous reparlerons de ca plus tard. Pour l'instant, il s'agit de savoir
utiliser ces méthodes. Le principe est des plus simples pour utiliser une méthode :
on s'en sert comme les propriétés, en s'assurant simplement qu'on y a accès en la
précédant par le nom du composant auquel elle appartient. Si ces méthodes ont des
paramètres, on les donne comme pour des procédures ou des fonctions.
Un petit exemple amusant pour commencer : créez un projet vierge et placez deux
boutons 'Button1' et 'Button2' sur la fiche principale (et toujours unique, mais
cela ne va plus durer très longtemps). Dans la procédure réagissant au clic sur
'Button1', entrez l'instruction ci-dessous :
Button2.Hide;
puis exécutez l'application. Lors d'un clic sur 'Button1', 'Button2' disparaît : il
se « cache » ('Hide' en anglais). Vous remarquerez qu'aucune propriété n'a été
modifiée par notre code puisqu'aucune affectation n'est écrite. Pourtant, la méthode
'Hide' des composants Button cache le bouton, et effectue donc en quelque sorte la
même chose que si l'on avait changé la propriété 'Visible' du bouton (propriété qui
décide si un composant est visible ou non).
Puisqu'on en est à l'amusement, mettez le code ci-dessous à la place du précédent :
Hide;
Sleep(5000
);
Show;
En exécutant cet exemple, l'application « disparait » pendant 5 secondes (5000
millisecondes, d'où le 5000 dans le code) puis réapparait comme avant. L'explication
est la suivante : la méthode 'Hide' nommée directement s'applique à la fiche : la
fiche se cache donc. Puis la procédure 'Sleep' fait « dormir » l'application pendant
5 secondes. Enfin, après ces cinq secondes, la méthode 'Show' fait réapparaître la
fiche.
Une méthode des fiches qui ne manquera pas de vous intéresser est la méthode
'Close'. Cette méthode ferme la fiche dont on appelle la méthode 'Close'. Il est
alors intéressant de savoir que l'application se termine lorsque l'on ferme la fiche
principale. Essayez donc, à la place du code précédent un peu fantaisiste, de mettre
l'instruction unique :
Close;
Exécutez l'application. Un clic sur le bouton ferme la fiche, et donc quitte
l'application : vous venez de créer votre premier bouton 'Quitter' !
Passons à un exemple plus croustillant : vous voyez régulièrement sous Windows des
listes d'éléments. Un moyen de créer ces listes sous Delphi (on parle ici des listes
simples, et non pas des listes complexes telles que la partie droite de
l'explorateur Windows) passe par le composant ListBox (type TListBox, dont l'icône
est ). Sur le projet déjà entamé, placez un
composant ListBox, redimensionnez le tout pour obtenir ceci :
Commençons par le plus simple : assurez-vous que le bouton 'Quitter' quitte
effectivement l'application (en générant la procédure répondant au clic et en
écrivant comme unique instruction « close »). Le bouton 'Lancer le test' nous
permettra d'effectuer divers tests de plus en plus élaborés.
Avant cela, un petit complément sur les ListBox s'impose. Les ListBox (zones de
liste) sont composées d'un cadre, dans lequel apparaissent un certain nombre de
lignes. Ces lignes comportent le plus souvent du texte. Un composant ListBox
comporte une propriété 'Items' qui centralise tout ce qui a trait à la gestion des
éléments de la liste. 'Items' n'est pas une simple donnée : c'est un objet. Pour
l'instant, nul besoin encore pour vous d'en connaître plus sur les objets, sachez
simplement qu'un objet se manipule comme un composant : il possède propriétés et
méthodes. Cette propriété 'Items' permet donc l'accès à d'autres propriétés et
méthodes internes. La méthode 'Add' de 'Items' permet d'ajouter une chaîne de
caractères à une zone de liste. Elle admet un unique paramètre de type chaîne de
caractères qui est ajouté à la liste. Pour appeler cette méthode, on doit écrire
:
composant_liste.Items.Add(Chaîne)
Utilisons donc cette méthode pour ajouter un élément dans la liste lors d'un clic
sur le bouton 'Lancer le test'. Servez-vous de l'extrait de code ci-dessous pour
complèter la procédure de réponse au clic sur ce bouton :
procedure
TForm1.Button1Click(Sender: TObject);
begin
ListBox1.Items.Add('Ca marche !'
);
end
;
La seule instruction présente dans cette procédure s'adresse à 'ListBox1' (que nous
aurions dû renommer en 'lbTest' par exemple). Elle ajoute la chaîne « Ca marche ! »
à la liste à chaque clic sur le bouton (essayez !).
Cet exemple ne permet cependant pas de vider la liste. Pour cela, il faudrait
utiliser la méthode 'Clear' de 'Items'. Essayez donc de remplacer l'ancien code par
celui-ci :
ListBox1.Items.Clear;
ListBox1.Items.Add('Ca marche aussi !'
);
La liste est vidée avant l'ajout de la chaîne, ce qui donne un effet visuel moins
intéressant, mais a pour mérite de vous avoir appris quelque chose.
Maintenant que vous connaissez le nécessaire, passons à un exercice grandeur nature
: un mini-projet.
Mini-projet n°2
(voir Indications, Solution et Téléchargement)
L'objectif de ce mini-projet est de vous faire créer une application exploitant les
notions que vous venez de découvrir, à savoir :
- Gestion de composants, propriétés et méthodes.
- Utilisation d'une zone d'édition, d'une liste.
Si vous avez suivi le début de ce chapitre, vous avez toutes les
connaissances nécessaires à la création de ce mini-projet. Des indications, une
solution guidée pas à pas ainsi que le téléchargement sont proposés ici.
Voici maintenant l'énoncé du problème.
Le but de ce projet est, à partir de l'interface que voici, composée d'une liste,
d'une zone de saisie et de trois boutons, de permettre les actions mentionnées
ci-dessous :
- Ajout de texte dans la liste : pour cela, on entre le texte dans la zone d'édition puis on clique sur le bouton. Le texte est alors ajouté à la liste et la zone de saisie est vidée (le texte y est effacé).
- Effacement de la liste : en cliquant sur le bouton adéquat, le contenu de la liste sera effacé.
- Fermeture de l'application, en cliquant sur 'Quitter'.
Bon courage !
IX-E. Evénements▲
Depuis le début de ce guide, je ne cesse de vous parler des événements, qui sont d'une importance considérable. En effet, les événements sont le coeur du style de programmation employé sous Delphi : les événements ne sont pas seulement une possibilité offerte aux programmeurs, mais presque un passage obligé puisque c'est le style de programmation privilégié sous un environnement graphique tel que Windows. Nous consacrerons donc un paragraphe entier à l'étude du style de programmation à l'aide d'événements, puis nous passerons à leur utilisation en pratique.
IX-E-1. Style de programmation événementiel▲
Aux débuts de l'informatique, l'utilisation des ordinateurs se résumait souvent à
concevoir un programme, le coder, l'exécuter et traiter les résultats. Depuis,
l'apparition des PC a permis de se ménager une certaine liberté quant à
l'utilisation d'un ordinateur. L'apparition de Windows, dans le monde du PC que
vous connaissez, a favorisé un style non directif : on n'oblige en aucun cas
l'utilisateur à suivre une démarche précise et prévisible : on sait seulement qu'il
sera susceptible d'accomplir un certain nombre d'actions, auxquelles on est apte à
réagir. Pour bien marquer la différence entre les deux grands styles de
programmation, à savoir la programmation directive et la programmation
événementielle, choisissons de réfléchir à un petit programme destiné à calculer
des additions.
Premier programme, en style directif : vous demandez un premier nombre, puis un
second, puis vous donnez le résultat. L'utilisateur ne peut rien faire d'autre que
vous donnez les informations et attendre le résultat, il n'a aucune liberté.
Second programme, en style non directif : vous donnez à l'utilisateur deux endroits
pour rentrer ses deux nombres, un espace ou sera affiché le résultat, et un bouton
qui effectuera le calcul. L'utilisateur n'est alors pas astreint à respecter
l'ordre normal : il peut entrer un premier nombre, en entrer un second, puis
constater qu'il s'est trompé et corriger le premier, quitter sans effectuer
d'addition, et calculer le résultat quand bon lui semble. Ce style paraît
avantageux mais oblige le programmeur à montrer plus de vigilence : l'utilisateur
peut sans le vouloir sauter des étapes dans le processus logique, en oubliant par
exemple de donner l'un des deux nombres (évidemment, avec cet exemple, c'est peu
vraisemblable, mais imaginez une boite de dialogue de renseignements sur une
personne, où l'on omettrait un renseignement indispensable, comme la date de
naissance).
Autre exemple directif : la procédure d'installation d'un logiciel : on vous guide
dans l'installation divisée en étapes, dont certaines vous proposent quelques
choix, mais sans vous permettre de passer directement à la copie des fichiers sans
passer par le choix des programmes à installer. Le style directif s'impose alors,
et on peut reconnaître son utilité car on voit mal un programme d'installation
commencer à choisir les fichiers à installer avant de rentrer un numéro de série
pour le logiciel.
Dernier exemple non directif : Windows en lui-même : vous êtes complètement libre,
sous Windows, de lancer telle ou telle application, et de faire ainsi ce que vous
voulez : à un moment vous travaillerez sous votre tableur préféré, et à l'autre
vous travaillerez à l'extermination d'envahisseurs extra-terrestres, deux
occupations que vous auriez pu choisir dans l'ordre inverse : vous êtes libre,
c'est le style non directif.
Sous Delphi, c'est le style non directif qui est privilégié, avec aussi bien
entendu la possibilité de créer un programme très directif. La programmation des
applications s'en ressent, et en permier lieu par l'utilisation des événements, ce
qui lui donne le nom de programmation événementielle. Ces événements permettent de
laisser l'utilisateur libre, et de simplement réagir selon ce qu'il fait : clic sur
un bouton, entrée de texte dans une zone de saisie, clic de souris, etc...
Il existe des événements de diverses nature. Un événement s'applique la plupart du
temps à un composant (mais aussi parfois à une application entière) : c'est le
composant ou la fiche qui déclenche l'événement. Delphi permet d'assigner une
procédure à chacun de ces événements, destinée à réagir à l'événement comme il se
doit.
Dans une application, de très nombreux événements se produisent. Il n'est pas
question de réagir à TOUS ces événements : lorsqu'on n'affecte pas de procédure à
un événement, le traitement de l'événement est fait pas Windows. Par exemple,
lorsque vous n'affectez pas de procédure à l'événement « clic de la souris » d'un
bouton, un clic produit toujours l'effet classique d'un bouton, à savoir
l'enfoncement puis le relâchement, mais rien de plus.
Ce qu'il est important de comprendre, c'est qu'on fait généralement fonctionner un
programme basé sur une interface en répondant à un éventail d'événements adaptés à
l'application. Un programme qui effectue des additions n'a que faire des mouvements
du pointeur de la souris, tandis qu'un logiciel de dessin aura tendance à
s'intéresser de très près à ces mouvements, transmis au programmeur sous forme
d'événements. Nous avons le plus souvent fait fonctionner nos programmes de test en
cliquant sur des boutons, ce qui nous a permis d'éxécuter à chaque fois une ou des
procédures.
C'est ainsi désormais que nous allons faire « vivre » nos programmes : nous
construirons l'interface, puis nous lui donnerons vie en répondant à certains
événements ciblés.
IX-E-2. Utilisation des événements▲
Venons-en maintenant à l'utilisation des événements. Ces derniers sont listés dans l'inspecteur d'objets, dans l'onglet « Evénements ». Comme pour les propriétés, la liste est spécifique à chaque composant : chaque composant, de même que chaque fiche, a sa liste d'événements. Il est possible d'assigner une procédure à un des événements d'un composant en suivant le processus suivant :
- Sélectionnez le composant qui déclenche l'événement
- Allez dans l'inspecteur d'objets (F11) dans l'onglet 'événements'.
- Effectuez un double-clic sur la zone blanche en face du nom de l'événement que vous souhaitez traiter. La procédure sera générée et appelée à chaque occurence de l'événement.
Les événements déjà attachés à des procédures montrent le nom de cette procédure en
face de leur nom. Le nom de la procédure est constitué par défaut du nom du
composant, puis du nom de l'événement auquel on a retiré le préfixe 'On' présent
dans tous les noms d'événements.
Suivant les événements, les paramètres de la procédure changeront. Une procédure
d'événement a toujours un paramètre 'Sender' de type 'TObject' que nous décrirons
plus tard mais qui ne sert à rien dans une bonne partie des cas.
Une fois que vous disposez de la procédure (son « squelette »), vous pouvez y
inscrire des instructions, y déclarer des constantes, des variables. Cependant, il
y a certains points sur lesquels vous devez faire attention :
- Lorsque vous compilez le projet, les procédures vides (sans déclarations ni instructions ni commentaires) associées aux événements sont automatiquement supprimées car parfaitement inutiles. La référence à cette procédure dans l'inspecteur d'objets est également supprimée car la procédure n'existe plus.
-
Pour supprimer une procédure associée à un événement, c'est-à-dire pour ne plus répondre à cet événement, il ne faut pas supprimer la procédure entière car Delphi serait dans l'incapacité de gérer cela. La procédure à suivre est la suivante :
- Supprimez toutes les instructions, commentaires et déclarations de la procédure : ne laissez que le squelette, c'est-à-dire la ligne de déclaration, le begin et le end.
- A la prochaine compilation, la procédure sera supprimée, de même que la référence à celle-ci dans l'inspecteur d'objets.
A la lumière de ces explications, vous comprenez certainement mieux maintenant le
pourquoi de la procédure de réponse au clic sur les boutons : double-cliquer sur un
bouton revient à double-cliquer dans l'inspecteur d'objets sur la zone blance
associée à l'événement nommé « OnClick ». Essayez pour vous en convaincre.
La quasi-totalité des événements que vous rencontrerez sous Delphi commencent par
le préfixe « On » : c'est un signe de reconnaissance des événements. Chaque
composant dispose de son propre ensemble d'événements, mais d'un composant à un
autre de même type (deux boutons par exemple), la liste des événements disponibles
sera la même, mais la réponse (la procédure associée) sera différente.
Par exemple, le type de composant Edit propose l'événement OnChange, alors que les
boutons ne proposent pas cet événement. Si vous utilisez plusieurs composants Edit,
vous pourrez associer une procédure à l'événement OnChange de chacun.
Permier exemple pratique : dans un projet vierge, posez un composant Edit sur la
fiche principale. Dans l'inspecteur d'objets, trouvez l'événement OnChange de ce
composant :
Double-cliquez sur la zone blanche : une procédure est générée : le code source devrait en être :
procedure
TForm1.Edit1Change(Sender: TObject);
begin
end
;
Et entrez l'instruction suivante :
Caption := Edit1.Text;
Lancez l'application et tapez quelque chose dans la zone d'édition : vous devez
constater que le texte dans la barre de titre de l'application est désormais le
même que celui que vous tapez dans la zone de saisie. L'explication est assez
simple : nous avons utilisé l'événement 'OnChange'. Cet événement se produit à
chaque fois que le texte de la zone de saisie (propriété 'Text') change. Dés que
vous tapez quelque chose, ou effacez du texte, la procédure 'Edit1Change' est
appelée. C'est cette procédure qui fait le changement : elle assigne à 'Caption'
(propriété de la fiche) la valeur de la propriété 'Text' de la zone d'édition
'Edit1', ce qui permet aux deux textes d'être identiques puisqu'à chaque
modification, l'un est fixé identique à l'autre.
Lorsque vous modifiez le texte dans la zone d'édition, cela change tout d'abord la
valeur de la propriété 'Text'. Ce changement fait, l'événement 'OnChange' est
déclenché, et appelle la procédure qui lui est attaché, qui modifie la barre de
titre de l'application. L'impression donnée par ce programme est de pouvoir fixer
le titre de l'application : l'utilisateur n'a pas conscience des événements
déclenchés par la saisie au clavier.
Difficile de faire un choix parmi les nombreux événements pour vous les montrer...
Jettons un oeil à l'événement 'OnMouseMove' : cet événement est déclenché à chaque
fois que la souris bouge « au dessus » du composant, en nous transmettant des
informations fort utiles, comme les coordonnées de la souris et l'état des touches
Shift, Control, Alt et des boutons de la souris. Cet événement a donc tendance à se
produire des dizaines de fois par secondes : il faudra bien faire attention à ne
pas y mettre d'instructions qui prendront trop de temps d'éxécution, sous peine de
résultats assez imprévisibles.
Ajoutez une deuxième zone d'édition au projet précédent, et nommez les deux zones
'edX' et 'edY'. Veillez à dimensionner la fiche pour pouvoir balader la souris sans
qu'elle souffre de claustrophobie. Nous allons utiliser l'événement 'OnMouseMove'
de la fiche pour suivre les coordonnées de la souris. Allez donc chercher
l'événement 'OnMouseMove' de la fiche :
Double-cliquez sur la zone blanche et entrez les deux instructions que vous pouvez voir ci-dessous :
procedure
TForm1.FormMouseMove(Sender: TObject; Shift: TShiftState; X,
Y: Integer
);
begin
edX.Text := 'X = '
+ IntToStr(X);
edY.Text := 'Y = '
+ IntToStr(Y);
end
;
Exécutez l'application : lorsque vous bougez la souris, les coordonnées relatives à la fiche s'inscrivent dans les deux zones d'édition. Le fonctionnement de l'application est simple : à chaque mouvement de la souris, l'événement 'OnMouseMove' est déclenché et transmet les coordonnées X et Y de la souris. Ces coordonnées sont transformées en chaînes de caractères, ajoutées à un texte de présentation et inscrites dans les deux zones d'édition. Vous remarquerez certainement que lorsque le pointeur passe sur les zones d'édition, les coordonnées ne sont plus mises à jour : le pointeur n'est en effet plus sur la fiche mais sur l'une des zones d'édition contenues dans la fiche.
IX-F. Composants invisibles▲
Certains composants ont une particularité des plus intéressante : ils sont
invisibles. Non pas que leur propriété 'Visible' soit fixée à 'False', non, ils ne
sont purement et simplement invisibles. Ces composants, lorsqu'ils sont placés sur
une fiche, sont représentés par un petit cadre montrant l'icône qui les représente
dans la palette des composants (nous parlerons dans ce guide de pseudo-bouton). Un
exemple est "MainMenu" : lorsque vous placez un tel composant sur une fiche, une
sorte de bouton est placé sur la fiche, mais si vous lancez immédiatement le projet
sans rien modifier, ce bouton n'est pas visible : le composant que vous venez de
placer est invisible.
Ce pseudo-bouton placé sur la fiche n'est visible que sous Delphi : c'est une sorte
d'interface entre vous et le composant qui est vraiment invisible. Cette interface
est faite pour vous permettre de sélectionner le composant, de changer ses
propriétés, ses événements. Déplacer ce pseudo-bouton n'a aucun effet sur le
composant : il est non visuel et n'a donc pas d'emplacement. Déplacez ces
pseudo-boutons si vous le souhaitez pour bien les ranger quelque part sur la fiche
si vous le voulez : ils ne seront de toute façon pas visibles à l'exécution de
l'application.
L'utilité de tels composants ne paraît pas forcément évidente au néophyte que vous
êtes peut-être : n'ai-je pas dit que les composants permettaient de créer une
interface ? Et une interface est visible, non ? Et bien comme partout, il y a des
exceptions : les composants invisibles rendent de nombreux services. Les plus
courants sont ceux présents sur l'onglet 'Dialogs' : ces composants invisibles
permettent l'accès à des fenêtres standard de Windows telles que les fenêtres
d'ouverture ou d'enregistrement de fichiers, de choix de couleurs : vous pilotez ces
fenêtres très simplement via un composant, au lieu de devoir recréer ces fenêtres
standards au moyen de fiches. Il n'est pas concevable de montrer ces fenêtres
standards à l'intérieur des fiches, ce qui force le choix de composants invisibles.
Vous pouvez utiliser ces fenêtres à volonté : les montrer, les cacher, sans que
l'utilisateur ne les ait sous les yeux en permanence.
Un dernier composant invisible dont vous devez connaître l'existence est le
composant "ImageList". Ce composant, invisible, permet de stocker une liste d'images
numérotées. Ce composant seul ne sert à rien, mais certains composants comme les
menus, les arbres ou les listes d'icônes en ont absolument besoin. Vous apprendrez
donc à vous servir de ce composant au chapitre 8, qui comporte plusieurs parties
dédiées au fonctionnement de ces composants invisibles.
IX-G. Imbrication des composants▲
Contrairement à ce que l'on pourrait penser, placer un ensemble de composants sur
une fiche ne suffit pas toujours à constituer une interface viable : il faut parfois
constituer des interfaces défiant cette optique. Pensez pour bien comprendre ce
point de vue à un classeur à onglets tel que la boite de dialogue d'options de Word
: plusieurs pages sont présentes sur la même fiche, la plupart sont cachés pour ne
laisser visible que le contenu de l'onglet sélectionné. Tout ceci serait evidemment
possible sans regrouper les composants, mais au prix d'efforts et de contraintes
dissuasives (il faudrait masquer et afficher manuellement un nombre incroyable de
composants à chaque changement d'onglet actif, ce qui serait non seulement long et
pénible, mais aussi une source intarissable d'erreurs et de bugs).
Le regroupement des composants est une des facettes de l'imbrication des composants
: il est en effet possible d'imbriquer certains composants dans d'autres. Il existe
à ce niveau deux familles de composants : ceux qui peuvent contenir d'autres
composants, et ceux qui ne le peuvent pas. Parmi ceux qui peuvent en contenir
d'autres, ceux auquels on ne pense pas sont... les fiches ! En effet, vous placez
depuis le début de ce guide des composants sur les fiches, qui SONT des composants
(spéciaux, il est vrai, mais non moins des composants tout à fait dignes de cette
appellation). Parmi ceux qui ne peuvent contenir d'autres composants, je citerai les
boutons, les cases à cocher, les zones d'édition, les listes, et beaucoup d'autres
qui constituent en fait la majorité des composants.
Les composants qui peuvent en contenir d'autres, dits composants « conteneurs »,
sont assez peu nombreux, mais d'un intérêt majeur : on compte, en dehors des fiches
les composants "Panel", "GroupBox", "PageControl" et "ToolBar" pour ne citer que les
plus utilisés.
Regrouper les composants au sein de composants conteneurs a plusieurs avantages :
- Avantage visuel : le composant "Panel" permet par exemple de créer des cadres en relief, mettant en évidence le regroupement des composants.
- Avantage logique : lorsque vous construirez un classeur à onglet sous Delphi, il sera plus naturel de sélectionner un onglet, puis d'y placer les composants qui devront apparaître lorsque cet onglet est actif. Changez d'onglet sous Delphi et seul le contenu de cet onglet est visible, laissant de coté le reste des composants des autres onglets.
- Avantage de conception : imaginez ce que serait l'interface d'une fiche si tous ses composants étaient visibles simultanément : ce serait inregardable, un fouillis incompréhensible.
- Avantage de placement : les composants contenus dans d'autres se déplacent avec leur conteneur, ce qui permet de déplacer facilement tout un ensemble d'un endroit à un autre.
Nous n'allons étudier brièvement ici qu'un de ces composants : le composant "Panel" (qui sera étudié plus en détail au chapitre 8). Ce composant est un conteneur, qui peut donc recevoir d'autres composants. Faites l'expérience : placez un "Panel" sur une fiche, puis placez deux composants identiques (des boutons par exemple), l'un sur la fiche (cliquez pour cela en dehors du "Panel" au moment de placer le bouton) et un sur le "Panel" (cliquez pour cela sur le panel pour placer le bouton. Vous devriez obtenir quelque chose de ce genre :
Essayez maintenant de déplacer le bouton placé à l'intérieur du panel : vous constaterez qu'il vous est impossible de franchir les bords du panel. Le panel constitue en fait une sous-zone de la fiche que vous pouvez agencer comme vous le voulez, et que vous pouvez ensuite placer ou aligner où vous voulez. Le composant Panel a un autre intérêt : avec les propriétés "BevelInner", "BevelOuter", "BevelWidth" et "BorderStyle", il est possible de constituer une bordure personnalisée des plus appréciable. Attention à ne pas tomber dans l'excés à ce niveau, ce qui est en général le propre des débutants, il suffit souvent d'une bordure très fine comme ci-dessous :
Essayez maintenant de déplacer le panel et non le bouton : vous voyez que le bouton reste à l'intérieur du panel, au même emplacement relatif au panel. Fixez maintenant la propriété "Align" du panel à "alBottom" : le panel est maintenant bloqué en bas de la fiche. Redimensionnez la fiche, et vous aurez l'agréable surprise de voir votre panel rester en bas de celle-ci, et se redimensionner pour coller parfaitement au bas de la fenêtre, comme sur la capture ci-dessous :
Grâce à un tel composant, il sera facilement possible de créer des boutons restant à
leur place en bas d'une fenêtre, quelle que soit la taille de celle-ci. Même si dans
certains cas l'utilisateur n'aura pas à redimensionner la fiche, vous, le
programmeur, aurez peut-être besoin de le faire, sans avoir à vous soucier des
détails du style emplacement des boutons.
Pour plus de détails sur l'utilisation des composants conteneurs, consultez les
parties du chapitre 8 consacrées à ces composants.
IX-H. Types de propriétés évolués▲
Cette partie présente succintement certains types particuliers de propriétés, ainsi que des caractéristiques importantes. Les notions abordées ici ne le sont que pour permettre d'aborder plus facilement le chapitre 8. Ces notions sont expliquées en détail dans le long chapitre consacré aux objets.
IX-H-1. Propriétés de type objet▲
Petit retour obligé ici sur les propriétés. La connaissance de l'utilisation des
méthodes étant indispensable ici, ce paragraphe a volontairement été placé en
dehors de celui réservé aux propriétés.
La plupart des propriétés que vous avez vu jusqu'ici sont de types connus par vous,
à savoir des types simples comme des booléens, des entiers, des chaînes de
caractères ou des types énumérés. Certaines propriétés, cependant, sont plus
complexes car elles sont elles-mêmes de type objet ou même composant. Ces
propriétés affichent dans l'inspecteur d'objets une valeur entre parenthèses du
style "(TStrings)" et un bouton . La propriété
"Items" des composants "ListBox", que vous avez déjà eu l'occasion de manipuler, en
est un bon exemple : en cliquant sur le petit bouton, on accède à une interface
spécifique d'édition de la propriété.
Ces propriétés sont certes un peu moins faciles à manipuler que les propriétés
simples, mais cela veut simplement dire qu'elles ont plus de puissance. On manipule
en fait ces propriétés comme on manipulerait un composant. Sous Delphi, une
interface d'édition sera proposée pour vous simplifier la vie. Au niveau du code,
on manipule ces propriétés comme tout composant, à savoir qu'elles ont des
propriétés et des méthodes mais en général pas d'evénements. Il est possible
d'utiliser de manière classique ces "sous-propriétés" et ces "sous-méthodes" (on
oubliera très vite ces vilaines dénominations pour ne plus parler que de propriétés
et de méthodes), comme vous l'avez fait avec la propriété "Items" des composants
"ListBox".
Pour accèder à une propriété ou à une méthode d'une propriété objet, il suffit
d'employer la syntaxe suivante qui doit vous rappeler celle utilisée avec le
composant "ListBox" :
composant.propriete_objet.propriete := valeur; // modification d'une propriété
composant.propriete_objet.methode(paramètres); // appel d'une méthode
Ces propriétés sont généralement employées dans des composants faisant intervenir
des listes, à savoir les "ListBox" que vous connaissez déjà, les "ComboBox" que
vous connaissez certainement aussi (liste déroulante), les arbres (pensez à la
partie gauche de l'Explorateur Windows), les listes d'icônes ou d'éléments a
plusieurs colonnes (pensez à la partie droite de l'explorateur windows), les Mémo
(pensez au bloc-notes : chaque ligne est un élément d'une liste).
Nous aurons de nombreuses fois l'occasion d'utiliser ces propriétés, il n'était
question ici que de les présenter brièvement.
IX-H-2. Propriétés de type tableau, propriété par défaut▲
Certaines propriétés que vous serez amenés à manipuler sont de type tableau. Vous
connaissez déjà les tableaux et vous savez donc déjà comment on accède aux éléments
d'un tableau. Les propriétés tableau ne sont pas de vrais tableaux, mais sont très
proches des tableaux, à savoir qu'on accède à leurs éléments de la même façon que
pour de vrais tableaux : par l'utilisation des crochets [] à la suite du nom du
tableau. Ici, il faudra simplement remplacer le nom du tableau par le nom d'une
propriété tableau, ce qui fait une différence théorique uniquement étant donné que
les noms de propriétés et de tableaux sont des identificateurs.
Pour clarifier la situation, voici un petit rappel avec un tableau classique :
procedure
TForm1.Button1Click(Sender: TObject);
var
Tableau: array
[1
..10
] of
integer
;
begin
Tableau[2
] := 374
;
ShowMessage(IntToStr(Tableau[2
]));
end
;
Je ne vous ferai pas l'affront d'expliquer ce que fait cet exemple simple. Voici maintenant un autre exemple démontrant l'utilisation d'une propriété tableau. Créez un projet vierge et placez une zone de liste ("ListBox") et un bouton sur l'unique fiche. Nommez la zone de liste "lbTest". Insérez le code présenté ci-dessous dans la procédure de réponse à l'evénement OnClick du bouton :
procedure
TForm1.Button1Click(Sender: TObject);
begin
lbTest.Items.Clear;
lbTest.Items.Add('Chaîne n°0'
);
lbTest.Items.Add('Chaîne n°1'
);
lbTest.Items.Add('Chaîne n°2'
);
ShowMessage(lbTest.Items.Strings[1
]);
end
;
Cet exemple est très riche dans le sens où il combine deux notions importantes, à
savoir les propriétés objet et les propriétés tableau. Dans cet exemple, la
propriété "Items" de "lbTest" est une propriété objet. Elle possède ses propriétés
et ses méthodes. Parmi les méthodes, "Clear" permet d'effacer le contenu de la
liste, et "Add" permet d'ajouter une chaîne (mais vous savez déjà cela si vous avez
suivi ce qui précède).
Ce qui est nouveau, c'est la propriété "Strings" de "Items" : cette propriété est
une propriété tableau. Les indices sont des entiers et les éléments du tableau sont
des chaînes de caractères, qui sont précisément les chaînes affichées dans la
liste. Comme beaucoup de propriétés tableau, "Strings" est indicée à partir de 0,
c'est-à-dire que l'élément 0 est le premier du tableau et que l'élément n est le
(n+1)-ième. Dans l'exemple ci-dessous, on prends le deuxième élément de la liste
(le deuxième ajouté), qui est lbTest.Items.Strings[1],
et on l'affiche, ce qui donne :
Les propriétés tableau sont des propriétés des plus utiles, et nous en reparlerons lorsque nous aborderons en détail les composants qui les utilisent. Pour l'instant, il me faut contenter certains lecteurs habitués à Delphi qui auront certainement tiqué à la lecture de l'exemple précédent. En effet, la dernière ligne de la procédure serait plutôt écrite comme cela par ces lecteurs :
ShowMessage(lbTest.Items[1
]); // le ".Strings" a disparu !
C'est une écriture valable, et pourtant comment expliquer qu'on puisse se passer
d'un morceau de code tel que ".items", étant donné qu'il fait appel à une propriété
? La réponse est que cette propriété, ne se satisfaisant pas d'être déjà une
propriété tableau d'une propriété objet, a une caractéristique supplémentaire :
elle est par défaut. Cette caractéristique réservée à une seule propriété
tableau par composant ou propriété objet permet de raccourcir l'accès à cette
propriété en n'écrivant plus explicitemet l'appel à la propriété tableau (qui est
fait implicitement dans ce cas). Dans le cas de la propriété "Items" des composants
"ListBox", les deux écritures suivantes sont donc parfaitement équivalentes :
lbTest.Items[1]
lbTest.Items.Strings[1]
La deuxième ligne est simplement plus longue que la première, c'est pourquoi on ne
se privera pas d'utiliser exclusivement la première.
IX-H-3. Propriétés de type référence▲
Certaines propriétés, vous le verrez, ne sont pas de vraies propriétés au sens où
nous l'entendons depuis le début : ce sont juste des références à d'autres
éléments, tels que des composants la plupart du temps. Contrairement au modèle
classique montré au début de ce chapitre, la propriété n'est pas entièrement
stockée dans le composant, mais se trouve en dehors de celui-ci. A la place, se
trouve une simple référence, un lien vers l'élément référencé, à savoir un
composant la plupart du temps.
Prenons un exemple : lorsque vous créérez un menu pour votre application, vous
utiliserez un composant "MainMenu". Vous serez certainement tentés de placer des
images à coté des éléments de menus, comme cela semble être la mode actuellement.
Ceci passe par l'utilisation d'un composant "ImageList" invisible. Ce composant
doit ensuite être mis en relation avec le menu par l'intermédiaire d'une propriété
"Images" de ce dernier. Vous n'avez plus ensuite qu'à piocher dans les images
présentes dans le composant "ImageList" pour les placer dans les menus (la création
de menus est décrite dans l'Annexe A du
chapitre 8).
Ce qui nous intéresse ici, c'est la propriété "Images" en elle-même. Cette
propriété est de type "TImageList", qui est précisément le type du composant
"ImageList". Mais ici, il n'est pas question de stocker le composant "ImageList"
dans le composant "MainMenu", puisque le premier est placé sur la fiche et est donc
stocké dans celle-ci. La propriété "Images" n'est donc qu'une simple référence au
composant "ImageList". Je sais que la nuance peut paraître inintéressante, mais
elle est de taille.
Quant à l'utilisation de ces propriétés références, c'est un vrai plaisir.
Supposons que notre menu soit nommé "MainMenu1" et notre "ImageList" soit nommée
"ImageList1". Les deux écritures suivantes sont alors équivalentes :
MainMenu1.Images
ImageList1
ce qui signifie simplement que vous pouvez utiliser une référence comme l'élément
lui-même. Il faudra cependant faire attention à vérifier qu'une référence est
valable avant de l'utiliser.
Nous reviendront en détail sur ces notions de références dans deux chapitres, l'un
consacré aux pointeurs et l'autre consacré aux objets. C'est un sujet qui ne peut
hélas qu'être effleuré tant qu'on n'a pas vu ces deux notions plus complexes.
IX-I. Conclusion▲
Ceci termine ce chapitre consacré à la manipulation des composants. Ce chapitre n'est en fait qu'une entrée en matière pour le chapitre suivant. Vous savez maintenant ce que sont les composants, les propriétés, les méthodes, les événements. Vous savez également comment utiliser ces éléments. Des composants particuliers comme les composants invisibles ou conteneurs ont été abordés. Ce qu'il vous manque maintenant, c'est une visite guidée des différents composants utilisables dans Delphi, avec les propriétés, méthodes et événements importants à connaître pour chacun d'eux. C'est l'objet du chapitre 8.