|
Rappel : Ce cours est enseigné dans la |
Jusqu'à
présent, lorsque nous voulions utiliser des contrôles dans une
application, nous avons toujours procédé de la même manière : au départ,
on crée tous les contrôles nécessaires à un moment ou à un autre dans
l'application, quitte à en cacher provisoirement quelques uns hors de la
vue de l'utilisateur. Et ensuite, on permet à l'utilisateur d'effectuer
certaines actions sur ces contrôles.
Cette
stratégie, dans 99 % des cas, donne des résultats tout à fait convenables.
Mais pour le 1% restant, il est bon de connaître une autre manière de s'y
prendre : celle qui va nous permettre de créer et de faire disparaître les
contrôles au cours de l'exécution de l'application.
Un exemple
de ce 1%, nous est donné par un jeu comme le démineur, que tout le monde
connaît pour l'avoir pratiqué de longues heures durant, à l'insu de son
chef de bureau. Au démineur, les règles du jeu sont invariables. Mais en
revanche, l'utilisateur a le droit de choisir entre plusieurs tailles de
damier. Pire, il peut même choisir librement le nombre de cases en ligne et
colonne.
Une manière
barbare de programmer cela, serait de créer au départ le plus grand damier
autorisé pour le jeu ; puis ensuite, à la lumière du choix effectué par le
joueur, de masquer les cases qui n'ont pas lieu d'être. Mais c'est une
solution d'une part bien peu élégante (et, question philosophique,
l'élégance n'est-elle pas la marque de tout bon programmeur ?), d'autre
part très lourde en termes de ressources mémoire, puisqu'on va mobiliser
de la place pour gérer des dizaines de contrôles qui s'avèreront le plus
souvent aussi superflus que le peigne de Fabien Barthez.
Il convient
donc de posséder la technique permettant de créer, de manipuler et de
détruire des contrôles par des instructions figurant dans l'application
elle-même. Ce n'est pas vraiment difficile, et en plus, c'est une
excellente manière de se préparer à la programmation objet proprement
dite.
Alors, comment procéder ? Réfléchissons un peu, ça ne pourra pas nous faire de
mal.
Si nous
créons un contrôle de toutes pièces au cours de l'exécution, et si l'on
veut que ce contrôle puisse servir à quelque chose, il va bien falloir
qu'existent les procédures d'événements qui lui sont liées.
Or, on n'a aucun moyen de créer en cours de route
lesdites procédures (un programme ne peut pas écrire des lignes
de programme). Ainsi, si l'on veut que notre contrôle soit autre chose
qu'un simple élément décoratif, il faut que les procédures événementielles
qui s'y rapportent aient été crées à l'avance. Comment est-ce possible ?
Tout simplement en ne créant dynamiquement que
des éléments de groupes, dont on aura préalablement défini le premier
élément et les procédures associées.
Quatre-vingt-dix-neuf fois sur cent, il faut donc appliquer la stratégie suivante :
Pour créer un élément supplémentaire d'un groupe de contrôles, on emploiera le code
suivant :
Load NomduGroupe(i)
où "NomduContrôle" est évidemment le nom du groupe, et "i" l'index
de l'élément qui sera créé.
Remarque importante : tout nouvel élément, créé par Load,
d'un groupe, est situé par défaut à l'emplacement exact de l'original, et
invisible. Il faudra donc le plus souvent modifier ses propriétés
Top et
Left, ainsi que sa propriété Visible.
Pour
supprimer un élément d'un groupe de contrôles, on écrira :
Unload NomduGroupe(i)
C'est aussi
simple que cela ! Pour peu qu'on ne fasse pas n'importe quoi, la création
et la destruction dynamiques de contrôles ne posent donc pas la moindre
difficulté. En fin de compte, pour conclure sur ce point : même si le
cheminement diffère un peu, les instructions Load
et Unload nous permettent de parvenir
au même résultat avec les groupes de contrôles que l'instruction Redim avec les tableaux de variables.
Rien à voir avec la haute couture de saison, voici pour conclure un concept fort utile dès que l'on est amené à gérer à la queue leu leu des hordes sauvages de contrôles.
Dans cet
esprit, on disposait déjà d'un outil simple, mais très performant : les
groupes ,
qui comme le disait le sage, à savoir moi-même, sont aux contrôles ce que
les tableaux sont aux variables. Eh bien, on pourrait dire, pour
filer la métaphore, que les collections sont aux contrôles ce que les
variables structurées sont aux variables : un assemblage d'éléments au
besoin hétéroclite, permettant un traitement global là où il fallait
auparavant prendre les éléments un par un.
Première
bonne nouvelle : tous les contrôles posés sur une
feuille appartiennent de droit à la collection de la feuille.
Au sein de cette collection, exactement comme dans un groupe, chaque
élément est désigné par un numéro d'indice
(correspondant ici à l'ordre de création de l'élément dans la feuille).
Pour
accéder aux éléments de la collection, on pourra employer la propriété
Controls de la Form, par exemple de la manière suivante :
Form1.Controls(5).Visible = False
...qui masquera le sixième contrôle créé sur Form1.
A noter que la propriété Controls n'est pas accessible depuis
la fenêtre des propriétés du mode conception, mais uniquement par le code.
Evidemment,
prendre les contrôles un par un par leur numéro d'indice, c'est bien, mais
le problème se pose vite de savoir à quel numéro s'arrêter (sinon, vlan,
c'est le dépassement d'indice direct garanti sur facture). Pour cela, deux
solutions :
Quelle que
soit la méthode utilisée, parcourir automatiquement toute une série, c'est
bien, mais pouvoir déterminer à quel type de
contrôle on a affaire, ce serait nettement mieux. Car pour
certains traitements, on a besoin de savoir si le contrôle est un bouton,
une zone de texte ou tout autre chose avant de pouvoir lui infliger quoi
que ce soit.
Eh bien, une fois de plus, VB a tout prévu, par l'instruction :
Typeof Bidule Is Nomtype
Où "Bidule" est un contrôle (ou une variable objet), et "Nomtype" un
mot-clé désignant un type (une classe) de contrôles : CommandButton,
TextBox, ListBox, etc.
Pour conclure, imaginons un fragment de code verrouillant toutes les zones de
texte d'une feuille, sans que ces zones de texte aient nécessairement été créées comme un
groupe de contrôle :
For Each Machin In Form1.Controls
If Machin Is TextBox Then Machin.Locked = True Endif Next Machin
Et le tour est joué. Puissant, n'est-il pas ?
Une application Windows digne de ce nom est fréquemment pilotée par des menus.
Avant d'aller plus loin, examinons quelques points de vocabulaire :
Concevoir des menus pour une application VB est d'une simplicité
confondante, dans la mesure où il existe un outil spécial : le créateur de
menus, accessible par la commande Outils - Créateur de Menus.
Ensuite, c'est une affaire de quelques minutes de prise en main.
Cet outil fait apparaître l'ensemble de la structure des menus d'une Form
donnée, et nous permet de la gérer. Le seul point à comprendre est que
chaque élément de menu, pour VB, est un objet, et à ce titre possède donc
un certain nombre de propriétés :
Dans la liste des menus, les niveaux des retraits indiquent le niveau des
menus. Les séparateurs correspondent tout simplement à des éléments qui
ont un Name, mais dont le Caption est un simple trait d'union.
Ensuite, chaque élément de menu, qui je le rappelle, est un objet, va
pouvoir déclencher un traitement par la programmation de la procédure qui
lui sera associée :
Private Sub NameElementMenu_Click()
... End Sub
Et voilà, c'est aussi simple que cela. Un petit entraînement ?
VB permet également de programmer des menus dits pop-up ou
contextuels. En
deux mots : après avoir créé le menu par le créateur de menus, il suffit
en quelque sorte de l'appeler par le code adéquat, en l'occurrence :
Popupmenu NameDuMenu, ConstanteVb
Cette instruction Popupmenu sera le
plus souvent placée dans une procédure MouseDown,
après un test vérifiant que c'est bien le bouton droit qui a été enfoncé.
Mais on peut imaginer des menus contextuels apparaissant à d'autres
occasions. Quant à la constante vb qui conclut l'instruction, elle indique
le positionnement du menu par rapport à la souris (voir l'aide VB sur ce point précis).
Enfin, pour qu'un menu soit exclusivement contextuel et n'apparaisse pas
en permanence dans la barre de menus du haut de la Form, il suffit de
mettre dans le créateur de menus sa propriété
Visible à False, et le tour
est joué.
Le bonheur, cela tient parfois à peu de choses.
Nous voilà armés pour faire sauter une nouvelle limite dans VB : celle de la form
unique.
En effet, jusqu'à présent, nos applications ne comportaient qu'une Form et une seule
à la fois. Même si nous disposions de plusieurs Form, celles-ci n'étaient
disponibles pour l'utilisateur que successivement : le code cachait une Form,
en rendait une autre visible, et c'était tout ce que l'on pouvait
faire.
Or, dans Windows, la plupart des applications utilisent plusieurs Form en même
temps, ou tout au moins laissent cette possibilité ouverte. Qu'on pense à
Word : il y a la fenêtre principale, celle de Word lui-même.
Mais ensuite, au sein de cette fenêtre, l'utilisateur peut ouvrir autant
de documents qu'il le souhaite, et chacun de ces documents apparaîtra dans
une nouvelle fenêtre.
Il est donc grand temps que nous apprenions à utiliser cette possibilité avec VB.
Une application VB gérant des fenêtres multiples est dite application
M.D.I. (pour Multiple Document Interface). Dans une telle application, il y
a obligatoirement une Form mère (et
une seule) et un certain nombre de Form filles,
contenues dans la Form mère.
Tout ceci peut être créé à la main, ou par du code... et vous comprenez pourquoi
nous abordons ceci après avoir vu les instructions Load,
Unload et la notion de Collection.
4.1 Création de la Form MDI
Commençons par la création à la main de la Form
mère. On choisira dans le menu adéquat une nouvelle Form, mais d'un type
particulier : la Form MDI. Celle-ci apparaît avec un fond spécial, plus
sombre qu'une Form classique :
Une Form MDI ne servant que de
conteneur aux Form filles, on ne peut pas poser dessus n'importe quels
contrôles. En fait, une telle Form ne peut contenir qu'un nombre très
limité de choses :
Si ce site n'explique pas
comment créer des barres d'outils, on vient en revanche de voir comment
créer des menus. Donc, pas de problèmes.
Pour finir sur les Form mères,
remarquons qu'il ne peut y en avoir qu'une et une
seule par projet VB. Si vous essayez d'en créer une deuxième,
VB vous l'interdira aussi sec. Logique.
Passons maintenant aux Form filles. Une Form
fille est une Form tout ce qu'il y a de plus normal. Elle a simplement une
caractéristique, c'est que sa propriété MDIChild
vaut True. C'est cela qui indique à
l'application que cette Form doit apparaître au sein de la Form mère de
l'application. Il peut bien entendu y avoir autant de Form filles que l'on
souhaite.
Cette histoire de mère et de filles, c'est donc vraiment... un jeu d'enfant (celle-là, je ne pouvais
décemment pas passer à côté).
Pour qu'à l'exécution, une Form fille apparaisse dans la Form mère, il suffit que la
Form fille soit l'objet d'une instruction Load. Pour la faire disparaître,
évidemment, l'instruction Unload s'impose.
4.2 Créer les Form filles par du code
Une Form étant un objet comme n'importe quel autre, on peut lui appliquer les
méthodes de création dynamiques vues au début de ce chapitre.
Malheureusement, il n'est pas possible de créer un groupe de Form, comme
nous le faisions pour les autres contrôles. Alors, serions-nous coincés ?
Que nenni. Si la première Form de notre application s'appelle FormIntérieure (propriété
Name), on pourra taper le code suivant pour créer une nouvelle Form (par
exemple, lorsque l'utilisateur clique sur un éventuel menu intitulé Nouveau) :
Dim Toto As New FormIntérieure
Toto.Visible = True
La première ligne crée une copie de l'objet FormIntérieure
(on parle en jargon objet de nouvelle instance),
qui possède en tout point les mêmes propriétés, les mêmes contrôles, etc.
Ce nouvel objet, copie conforme de FormIntérieure, est stocké dans une
variable objet qui permet de le désigner,
en l'occurrence Toto.
Petit souci : toutes les
nouvelles Form ainsi créés vont être successivement désignées par la
variable Toto. Et comme elles ne forment pas un groupe, on est a priori
bien en peine de les différencier.
A cela, il existe deux parades
simples, efficaces et de bon goût.
Lorsqu'il s'agit de traiter toutes les Form d'une application, on pourra utiliser la notion de
collection vue plus haut, et écrire une boucle adéquate :
For Each truc In Forms
truc.BackColor = vbWhite Next truc
...La collection Forms nous permettant de balayer
toutes les Forms de l'application une par une.
Enfin, dernier problème abordé
ici, lorsque nous avons besoin de localiser un contrôle d'une application
MDI, nous ne savons pas forcément de quelle Form est issu ce contrôle.
Imaginons que nous voulions, à un moment donné, changer ce qu'il y a écrit
sur le bouton de commande Bouton1. On
va alors être obligé, faute de provoquer une erreur, de préciser qu'il
s'agit du contrôle Bouton1 de la Form fille active, par opposition aux
autres boutons Bouton1 des Form filles
non actives). Car à ce moment là de l'application, il y a autant de Bouton1 que de Form filles dans
l'application ! Pour parler du Bouton1
de la Form active, rien de plus simple :
ActiveForm.Bouton1.Caption = "coucou"
Vous voyez, pour conclure, que
VB nous ouvre assez facilement les portes de la réalisation d'applications
au look vraiment professionnel. Après, plus ça grossit, plus il faut être
méthodique pour ne pas s'y perdre...
|