Une remarque pertinente ?
Une critique impertinente ?
Un lynchage en règle ?
Une invitation sous les tropiques ?

Ecrivez-moi !
Conçu et enseigné tel qu'en lui même, avec pertes, fracas et humour de qualité supérieure            
 par Christophe Darmangeat dans le M2 PISE du Master MECI (Université Paris 7)            

 
 
 
  
 
 
 
Partie 3
Premiers contrôles

Nous en savons à présent suffisamment pour commencer à mettre pour de bon les mains dans le cambouis. Ouvrons donc la boîte à outils, et regardons ça de près. Que trouvons-nous ?
1. La classe Form
La classe Form (qu'on appelle également le formulaire), issue de la classe Windows.Form, est l'élément de base, obligé et fondamental, de toute application VB pour Windows. C'est sur une Form, et uniquement sur une Form, que nous pourrons éventuellement poser d'autres contrôles. Et même si on peut à la rigueur la rendre invisible (mais il faut être un peu tordu), de toutes façons, elle est quand même là. Corollaire de cette proposition, c'est également dans le code correspondant à la Form que seront rassemblées toutes les procédures événementielles liées aux contrôles que nous aurons créés sur cette Form.
Nous pouvons d'entrée noter plusieurs propriétés de la classe Windows.Form (donc de tous les objets Form créés à partir de cette classe), propriétés que nous retrouverons dans la plupart, sinon dans la totalités des autres contrôles proposés par VB :
  • Name : il s'agit du nom de l'objet, exactement comme une variable porte un nom de variable. La valeur de cette propriété n'est donc pas visible à l'écran : il s'agit d'un nom qui sert uniquement dans le code, pour désigner l'objet en question.
  • Text : une autre propriété quasi-universelle des contrôles est le texte qui leur est associé à l'écran. Pour une Form, il s'agira du texte qui figure en haut, dans la barre de titre. Pour un bouton, ce sera le texte écrit dessus, pour une case, le texte qui figure juste à côté, etc. à l'inverse de la propriété Name, Text ne joue aucun rôle du point de vue du code, et un rôle essentiel du point de vue de l'interface.
  • Une erreur (grossière) à ne pas faire :
    Il ne faut évidemment pas confondre les propriétés Name et Text, dont le rôle n'a vraiment rien à voir.
  • Size : Il s'agit évidemment de la taille, autre propriété partagée par la presque totalité des contrôles. La propriété Size est d'un type particulier. Il s'agit en effet non d'un type simple, mais d'une structure, composé de deux Integer. Pour modifier sa valeur par du code, c'est donc un peu sportif : il faut utiliser une variable de ce type spécial Size déjà déjà existante, ou, le plus souvent, la créer à la volée par le constructeur New. Pour fixer par exemple la taille de la Form à 500 pixels sur 300, et sachant que la manière la plus simple de désigner la Form dans une des procédures qui lui sont liées est d'employer le mot Me, on pourra écrire :
  • Me.Size = New Size(500, 300)
    Heureusement, il y a un moyen de contourner l'obstacle et de se simplifier considérablment l'existence. Plutôt que nous embêter avec ce type Size structuré, nous pouvons, en fouinant un peu - découvrir que nous avons directement accès à deux autres propriétés, Height et Width, beaucoup plus maniables car de type Integer. Ainsi, la ligne ci-dessus pourra avantageusement être remplacée par :
    Me.Width = 500
    Me.Height = 300
    ...ce qui n'est tout de même pas plus tordu, loin s'en faut.
    Remarque pinailleuse :
    La propriété Size désigne les dimensions extérieures d'un contrôle. Si l'on désire connaître, ou définir, ses dimensions intérieures (la zone « cliente » du contrôle), on dispose de la propriété Clientsize, qui possède la même structure.
    La différence entre Size et Clientsize est particulièrement sensible avec les Form, en raison de l'existence de la barre de titre. 
  • Tant qu'on en est aux propriétés de type structurés (et surtout, aux moyens simples de contourner cet obstacle, une autre propriété constitue un must : il s'agit de Location, qui désigne l'emplacement d'un contrôle donné - tout comme Size, cette propriété est partagée par à peu près tous les contrôles de l'univers connu et habité. Location est elle aussi composée de deux entiers, qui correspondent au nombre de pixels qui séparent le contrôle respectivement du haut et de la gauche de son conteneur. Pour la Form, le conteneur est l'écran. Pour un contrôle posé sur la Form, le conteneur est la Form (on verra plus tard qu'on peut aussi avoir des contrôles sur une Form qui sont eux-même conteneurs d'autres contrôles). Quand une Form a sa propriété Location à (0, 0) elle est en haut à gauche de l'écran. Quand cette propriété d'un contrôle vaut (0, 0), il se trouve en haut à gauche de la Form, etc. Comme je le disais, là encore on peut contourner l'obstacle et on n'est pas obligés de passer par cette propriété structurée et donc embêtante à manipuler. Celle-ci se décompose en Top et Left, propriété numériques simples. Pour envoyer en cours d'exécution une form en haut à gauche de l'écran, on aura donc le choix entre :
  • Me.Location = New Location(0, 0)
    ...et le plus facile :
    Me.Top = 0
    Me.Left = 0
    Remarque millimétrée :
    Avec Top, Left, Width et Height, on dispose d'un moyen très simple de dimensionner et positionner n'importe quel contrôle en cours d'exécution. Pour une fois qu'on a une arme simple et efficace, on aurait bien tort de l'oublier !
  • Visible : cette petite propriété booléenne rend des services inestimables, puisqu'elle permet de masquer (quand elle vaut False) n'importe quel contrôle
  • Tag : voilà, pour finir, une bien étrange propriété. Tous les contrôles la possèdent, et pourtant, elle ne sert... à rien. Mais c'est là son principal atout : elle va pouvoir servir à tout ce qu'on veut ! Elle va nous permettre d'associer n'importe quelle valeur (de type String) à nos contrôles, et d'en faire bon usage. Nous verrons au cours de ces leçons plusieurs occasions dans lesquelles cette propriété s'avèrera fort précieuse.
D'autres propriétés de la classe Form sont propres à cette classe, et ne se retrouvent pas - ou rarement - dans d'autres classes. Il s'agit par exemple de :
  • Startposition : qui détermine la position de la fenêtre sur l'écran lors du lancement de l'application.
  • BackgroundImage : qui permet de désigner une image d'arrière-plan pour une Form.
  • FormBorderStyle : qui détermine le type de bordures utilisé
Côté événements, les Form sont capables d'en recevoir - autrement dit, d'en gérer - un certain nombre. Parmi ceux-ci, les plus importants, pour commencer, sont sans doute Load et Activate. Je m'y arrête d'autant plus volontiers qu'on a souvent une fâcheuse tendance à les confondre, alors qu'ils sont loin d'être identiques.
Définitions  :
L'événement Load correspond au chargement de la fenêtre en mémoire vive.
L'événement Activate correspond au fait que la Form spécifiée devient la fenêtre active.
Dans le cas d'une application qui ne compte qu'une seule Form, les deux événements se confondent. En effet, l'unique Form se charge au lancement de l'application, devenant par la même occasion la Form active, et le restant sans discontinuer jusqu'à la fin de cette application.
Mais dès qu'une application compte plusieurs Form, les choses se passent tout autrement. Toutes les Form vont en effet être chargées au lancement de l'application (déclenchant ainsi les événements Load une fois pour toutes). Mais une seule sera active. Par la suite, à chaque fois que l'utilisateur passera d'une Form à l'autre, il redéclenchera l'événement Activate pour la Form sur laquelle il vient d'atterrir.
Les événements Activate et Load se prêtent particulièrement bien à des instructions d'initialisation : c'est là, avant que l'utilisateur ait eu le temps de faire quoi que ce soit, qu'on remet les compteurs à zéro, qu'on remet les cases et les zones de saisie à blanc (ou qu'on y réécrit les valeurs par défaut), etc.
2. La classe Button
Les spécialistes de la langue de Chexpire auront bien sûr reconnu derrière ce nom sibyllin la classe servant à créer les... boutons.
On retrouve pour les contrôles créés à partir de la classe Button les propriétés générales déjà évoquées ci-dessus pour les Form : Name, bien sûr, mais aussi Text, Size, Location, Visible et bien d'autres.
Si la classe Button nous intéresse, ce n'est pas tant pour ses propriétés (ce n'est pas lui faire injure que de remarquer qu'elles restent somme toute limitées) que pour sa capacité à gérer un certain nombre d'événements, à commencer par le plus fréquent d'entre eux : Click. Nous allons donc pouvoir gérer des procédures déclenchées automatiquement par le clic sur un bouton, en suivant simplement les règles de syntaxe exposées précédemment ici-même.
Avec tout ce que vous avez lu jusque là, vous avez largement de quoi commencer à vous dérouiller les pattes (et les neurones).
Pour réussir ces exercices, vous disposez de presque toutes les connaissances nécessaires.
La seule exceptions concerne les deux versions du Cache-cache, qui correspondent au même résultat obtenu par deux stratégies différentes (soit on a deux boutons et on les masque alternativement, soit on a un seul bouton qu'on déplace). Dans les deux cas, vous allez tout de même avoir besoin de savoir comment on écrit les tests, sujet sur lequel ce passage du prochain chapitre éclairera votre lanterne.
Exercice
Exécutable
Sources
Télécommande
Cache-cache 1
Cache-cache 2
Moulin
Indiana Jones
3. Une parenthèse nécessaire : le paramètre Sender
Nous interrompons à présent le cours normal de nos émissions pour un flash d'information. Il est en effet grand temps de dire quelques mots d'un des deux paramètres en entrée qui ponctuent toutes les procédures événementielles que nous avons créées : je veux parler de Sender (en anglais : « l'envoyeur »).
En fait, si les procédures événementielles ne pouvaient gérer qu'un seul événement à la fois, ce paramètre ne servirait strictement à rien. Mais le truc, c'est que les procédures peuvent, si on le souhaite, gérer plusieurs évenements à la fois. On peut imaginer, par exemple, une procédure appelée Truc, qui serait déclenchée aussi bien par un clic sur Bouton1 que sur Bouton2, et dont la ligne de titre aurait alors la physionomie suivante :
Private Sub Truc(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Bouton1.Click, Bouton2.Click
En pareil cas, on pourrait avoir besoin, au sein de cette procédure, de savoir lors d'une exécution donnée quel est l'objet qui est à l'origine de cette exécution, autrement dit lequel, de Bouton1 ou de Bouton2, a été cliqué. Eh bien, c'est à cela que sert Sender.
On remarque que ce paramètre est défini par un drôle se type : System.Object. Autrement dit, Sender est une variable objet, c'est-à-dire une une variable qui pointe sur un objet. Et en l'occurrence, pas n'importe lequel : celui qui a déclenché l'exécution de la procédure. On peut donc dire que selon les cas, Sender vaudra Bouton1 ou Bouton2. Et on va pouvoir utiliser cette variable à l'intérieur de la procédure, soit en lecture (pour récupérer une information concernant l'objet, par exemple son Name) soit même en écriture (pour modifier une ou plusieurs propriétés de l'objet pointé).
Sender pointant sur un contrôle, cette variable possède en effet la caractéristique remarquable de posséder toutes les propriétés et les méthodes de l'objet qu'elle désigne. On pourra donc, par exemple, parler de
Sender.text
ou de
Sender.visible
etc.
Remarque sioux :
J'entends déjà une objection qui se veut définitive : « comment pourrait-on utiliser une propriété de Sender en écriture, puisque ce paamètre est passé à la procédure ByVal, donc en lecture seule ? ». Cela paraît imparable, mais en réalité, il y a un détail qui change tout : c'est que Sender n'est pas le contrôle lui-même, mas seulement un pointeur qui le désigne. Ainsi, le fait qu'il soit passé par valeur interdit qu'on change l'objet désigné par la variable Sender. Mais il n'interdit pas qu'on puisse, en quelque sorte par ricochet, modifier des propriétés de cet contrôle.
Vous pouvez à présent reprendre Indiana Jones en faisant en sorte de simplifier le code.
Exercice
Exécutable
Sources
Indiana Jones
4. La classe Label
On peut maintenant reprendre le cours de notre propos, et commençant par évacuer deux considérations essentielles.
  1. Oui, le Label est bien originaire de Cadix.
  2. Et oui aussi, le nom de prédilection pour un Label est effectivement Hélène.
Ces points étant désormais réglés, nous pouvons passer aux choses sérieuses et parler brièvement de ce contrôle, qui correspond donc à une simple étiquette posée sur une Form (généralement, pour éclairer l'utilisateur sur tel ou tel point, commenter une image ou une zone, etc.)
L'élément sans doute le plus important à retenir pour les Label est qu'il ne s'agit pas de zones de saisie pour l'utilisateur. Ce qui n'empêche pas le contenu d'un Label de pouvoir être modifié par le code en cours d'exécution, ni le Label de pouvoir recevoir un certain nombre d'événements, dont le Click.
Signalons toutefois trois propriétés que possède la classe Label :
  • Autosize : propriété booléenne qui active (ou désactive) le redimensionnement automatique du Label en fonction de son contenu - ce qui peut éviter par exemple qu'une partie du texte déborde du contrôle, et devienne ainsi illisible.
  • Borderstyle : propriété qui permet de régler le style - ou l'absence - de bordure autour d'un Label.
  • Textalign : propriété qui règle l'alignement du texte au sein d'un Label (il y a 9 possibilités, en fonction de l'alignement horizontal et vertical)
Et voilà tout ce qu'il y a d'important à dire sur la classe Label pour le moment.
5. La classe LinkLabel
Il s'agit d'une sous-classe de la classe Label. En termes de programmation objet, on dira que la classe LinkLabel hérite de la classe Label. L'héritage est un concept très important quand on commence à faire sérieusement de la programmation objet. Et on a beau être contre l'héritage dans la vie en général, il faut reconnaître qu'en programmation, c'est une invention très utile.
On reviendra plus loin et (un peu) plus en détail sur ce qu'est l'héritage, mais on peut profiter de l'occasion que nous donne le LinkLabel pour donner une première définition : une classe qui hérite d'une autre (on parle alors d'une classe fille qui hérite d'une classe parente) possède toutes les propriétés et méthodes de cette classe parente, plus d'autres. En l'occurrence, un contrôle LinkLabel possède toutes les propriétés et méthodes d'un contrôle Label, avec quelques facultés supplémentaires.
Cette aptitude supplémentaire des LinkLabel, c'est de gérer un lien hypertexte, qui va permettre d'ouvrir un navigateur et d'aller à l'adresse web indiquée. Par défaut, c'est l'ensemble du texte du LinkLabel qui sert de lien hypertexte, mais on peut très bien paramétrer l'affaire pour que seule une portion du texte joue ce rôle. Et si cette portion est réduite à rien, alors le lien est tout simplement désactivé.
Voici donc les principales propriétés propres à la classe LinkLabel  :
  • LinkArea : encore une propriété structurée en deux Integer. Le premier désigne le caractère à partir duquel commence le lien, le second le nombre de caractères qui compose le lien. Si la deuxième valeur vaut zéro, alors il n'y a aucun lien actif dans le texte du LinkLabel. Ne pas oublier la syntaxe un peu exotique liée à ces propriétés structurées :
    LinkLabel1.LinkArea = New LinkArea(integer1, integer2)
  • LinkBehaviour : propriété qui règle le comportement du soulignement pour le lien (standard, en cas de survol, toujours ou jamais)
Il y a une série d'autres propriétés, liées notamment aux différentes couleurs que doit prendre le lien selon les situations (actif, visité, etc.), mais elles ne devraient vous poser aucun problème.
En revanche, il faut maintenant ajouter ce qui concerne le lancement du navigateur avec l'url correcte. Car en réalité, à proprement parler, la classe LinkLabel en est incapable : tout ce qu'elle sait faire, c'est générer un événement LinkClicked, qui ne se produit que lorsque le lien est actif et que l'utilisateur a cliqué dessus.
Il nous faudra donc gérer la procédure correspondant à cet événement, en y introduisant l'instruction permettant d'ouvrir le navigateur avec l'url correcte, c'est-à-dire en y entrant le code suivant :
System.Diagnostics.Process.Start("url souhaitée")
Bon, ben... c'est reparti pour un tour !
L'exercice qui suit ne présente aucune difficulté particulière. Il n'implique ni astuce, ni connaissance qui ne figurerait pas dans les lignes précédentes.
En revanche, c'est une excellente occasion de vérifier que vous avez les idées bien en place en ce qui concerne les propriétés, les procédures, les événements, les instructions, toussa, quoi, et que du coup, vous pouvez vous en servir autrement qu'en jetant le tout pêle-mêle au hasard et en caressant l'écran avec un trèfle à quatre feuilles en espérant que ça marche.
Exercice
Exécutable
Sources
www