Rappel : Ce cours est enseigné dans la
spécialité PISE du Master MECI (Université Paris 7)
par Christophe Darmangeat

Partie 6
Objets, propriétés, evenements
Ce chapitre fourre-tout a pour but de faire un tour relativement complet des propriétés et des événements les plus utiles pour les différents objets. Bien entendu, vous avez déjà eu l’occasion de croiser un certain nombre de ces choses, mais là, vous allez repartir avec de quoi faire votre petit marché en toute quiétude.
1. Deux objets de curiosite
1.1 Un contrôle particulier : le Timer
Il s’agit du petit chronomètre situé dans la barre de contrôles. Ce contrôle joue un rôle fondamental et indispensable : c’est lui qui va permettre d’effectuer des traitements, à intervalles fixés d'avance, indépendamment des actions effectuées par l’utilisateur.
Jusqu’à présent, en effet, le code ne s’exécutait (si l’on met de côté le Form_Load et une éventuelle procédure principale située dans un module) qu’au cas où l’utilisateur faisait quelque chose : saisie au clavier, mouvement ou clic de souris, etc. Or, on peut tout à fait avoir besoin, dans certaines applications, que le code se mette en route, même si l’utilisateur ne fait rien du tout.
Le contrôle Timer, graphiquement toujours invisible, va générer des événements, des "tops", à une cadence choisie par le programmeur, qui déclencheront la procédure NomDuTimer_Timer(). Le programmeur pourra donc mettre dans cette procédure tout ce qui doit se passer indépendamment de ce que fera – ou ne fera pas - l’utilisateur.
Un Timer est très utile dans certains jeux, soit qu’on ait besoin de chronométrer quelque chose, soit qu’on ait besoin de provoquer certains événements à intervalles réguliers (un déplacement d’un bidule, la survenue d’un événement du jeu tiré au hasard, etc.)
La propriété essentielle d'un Timer est Interval. C'est elle qui fixe l'intervalle entre chaque top, exprimé en millisecondes).
 
Nom de l'exercice
Exécutable
Sources
Compte Harbour
1.2 L'objet "Application" : App
Ce jour est à marquer d'une pierre blanche, car voici notre première rencontre avec un objet qui n'est pas un contrôle, et qui ne possède donc aucune existence sous forme de représentation graphique. Loin des yeux, mais pas loin du cœur, voici l'objet App, qui n'est autre que l'ensemble de votre application elle-même, en personne. App, comme tout objet, possède un certain nombre de propriétés. L’une d’entre elles est particulièrement utile : il s'agit de la propriété Path, accessible uniquement en lecture, et qui indique quel est le chemin d’accès de l'exécutable.
Ceci se révèle particulièrement utile quand on a besoin d'aller chercher d'autres documents (fichiers textes, images à charger, etc.) dans une application. On ne sait jamais a priori comment s'appelle le répertoire dans lequel votre application a été installée sur d'autres machines que la vôtre. Grâce à l'objet App, et à sa propriété Path, on va donc pouvoir récupérer l'emplacement de l'exécutable, et de là, pointer le chemin conduisant au fichier que l'on veut manipuler.
Donc, en résumé, on peut utiliser cette propriété pour désigner un fichier par référence relative au répertoire dans lequel a été installé l'application, quel que soit le répertoire en question.
Admettons par exemple que vous ayez besoin d'aller désigner un fichier au cours d'un programme, parmi une dizaine disponibles (genre mois01.txt, mois02.txt, etc.). Vous mettez donc le nom du fichier à aller chercher dans une variable nommée Fic. Mais comment préciser à l'ordinateur le chemin menant à ce fichier ? Ce chemin sera a priori différent selon la manière dont votre application aura été installée sur chaque machine. La parade consistera donc :
  • à obliger l'application à installer la série des fichiers txt toujours au même endroit par rapport à l'exécutable (on supposera ici qu'ils sont dans un sous-répertoire nommé Files). On verra plus loin comment réaliser cette prouesse.
  • à désigner dans le code le chemin du fichier en récupérant celui de l'exécutable, et en pointant ensuite le sous répertoire Files.
Illustration :
' La variable Fic stocke le nom du fichier à ouvrir
Chemin = App.Path
If Right(Chemin, 1) <> "\" Then
  Chemin = Chemin & "\"
EndIf
Complet = Chemin & Fic
MsgBox "L'emplacement complet est : " & Complet


2. Propriétés
On n’examinera ici que certaines des propriétés les plus courantes, celles que l’on retrouve pour la majorité, sinon la totalité, des contrôles. Bien entendu, vous avez déjà expérimenté un certain nombre de ces choses, et il ne s'agit là que d'un petit complément..
2.1 Localisation des contrôles
Tout contrôle placé sur une Form possède deux propriétés qui régissent son emplacement :
  • Top : distance séparant le bord supérieur du contrôle du haut de la Form
  • Left : distance séparant le bord gauche du contrôle de la gauche de la Form
Comme vous le savez aussi, il y a également deux propriétés qui règlent la taille du contrôle. Ce sont :
  • Width qui spécifie sa largeur
  • Height qui stipule sa hauteur
Avec ces quatre propriétés, on règle donc et la taille et l’emplacement de tout contrôle, et on peut faire des choses graphiquement très joulies. CQFD.
2.2 Activation des contrôles
La plupart des contrôles possèdent les propriétés suivantes :
  • Enabled : permet / interdit l’action de l’utilisateur sur le contrôle considéré. Bon nombre de contrôles (mais pas tous) deviennent grisés lorsque cette propriété est False. Vous l'aurez deviné, il s'agit d'une propriété booléenne.
  • Visible : affiche / cache le contrôle considéré (et du même coup, permet / interdit) au contrôle de recevoir des événements clavier ou souris. C’est fou ce qu’on peut faire avec ce petit machin là. Comme la précédente, cette propriété est booléenne.
  • Tabindex : règle l’ordre de tabulation des contrôles dans la Form (propriété de type Integer).


3. Evénements
On va dans cette partie aborder des événements jusque là injustement dédaignés.
Je passe rapidement sur le Click et le DblClick, qui n’ont plus de secrets pour vous. Toutefois, c'est le moment de préciser un détail important : un contrôle ne peut pas à la fois posséder une procédure liée au click (simple) et au double click, car un des deux événements intercepte toujours l'autre. clic ou double clic, le concepteur d'interface doit donc choisir entre les deux !
Plus intéressants, car moins connus, en voici quelques autres :
3.1 Evénements Focus
Le focus est le fait, pour un contrôle, d’être celui qui est actuellement désigné par la tabulation (un petit liseré en pointillé apparaît autour d’un objet lorsqu’il a le focus, ou dans le cas des zones de texte, le curseur clignote à l'intérieur). Deux événements sont liés à la réception ou à la perte du focus :
  • Gotfocus : quand l’objet reçoit le focus
  • Lostfocus : quand l’objet perd le focus
J’en profite négligemment au passage pour signaler qu’une instruction permet de forcer le passage du focus à tel ou tel contrôle. Il s’agit de la méthode Setfocus. Ainsi, la ligne de code :
TrucMuche.Setfocus
envoie de gré ou de force le focus sur le contrôle TrucMuche.
3.2 Evénements clavier
On peut être amené, dans une application, à vouloir "guetter" la frappe de touches du clavier par l’utilisateur. Ceci recouvre grosse moto deux cas :
  • On veut gérer la saisie caractère par caractère dans un contrôle Textbox ou ComboBox. Ceci pour contrôler au fur et à mesure la validité des caractères frappés (éliminer les chiffres, par exemple…) ou convertir les caractères au fur et à mesure les caractères frappés (les convertir automatiquement en minuscules ou en majuscules, par exemple…). Dans ce cas, on pourra gérer les événements KeyPress KeyUp ou Keydown (voir plus loin pour le choix) au contrôle concerné.
  • On souhaite affecter, indépendamment de tout problème de saisie dans un contrôle, certaines actions à certaines touches du clavier. Typiquement, affecter les touches de fonctions : F1 pour ouvrir l'aide, etc. Ici, c’est à la Form que l’événement clavier devra être associé. Mais cela ne fonctionnera que si la propriété KeyPreview de la Form possède la valeur True. Dans ce cas, la Form reçoit ces événements avant les contrôles qu'elle contient.
La différence fondamentale entre KeyPress d’une part, et Keydown et Keyup d’autre part, c’est que KeyPress considère quel caractère a été frappé (le résultat logiciel de la frappe), alors que Keydown et Keyup considèrent l’état physique du clavier.
Par conséquent : KeyPress ne reconnaît pas les frappes ne produisant pas directement un caractère, comme les touches de fonction, les touches de modification, les touches de navigation et toutes les combinaisons de ces touches avec les modificateurs du clavier. En revanche, Keydown et Keyup gèrent parfaitement tout cela.
Autre différence, KeyPress interprète la majuscule et la minuscule de chaque caractère comme des codes de touche distincts et, par conséquent, comme deux caractères différents. KeyDown et KeyUp, quant à eux, interprètent la majuscule et la minuscule de chaque caractère au moyen de deux arguments : keycode, qui indique la touche physique (A et a étant alors équivalents) et shift, qui indique l'état de shift + key et qui renvoie en conséquence soit A, soit a.
Dernière chose sur ce chapitre, Keydown détecte le moment où une touche est enfoncée, KeyUp celui où elle est relâchée.
La gestion fine des événements clavier restant une chose relativement pénible en Visual Basic, nous passons rapidement à quelque chose de beaucoup plus répandu…
3.3 Evénements souris
MouseDown et MouseUp sont deux événements détectant respectivement le fait qu’un bouton de la souris a été enfoncé ou relâché au dessus d’un objet.
Un aspect fondamental de ces événements, c'est que la procédure qui leur est associée possède un certain nombre de paramètres. C'était déjà le cas avec les événements claviers ci-dessus, mais j'avais alors jeté un voile pudique sur cet aspect. En tout cas, là, avec la souris, on n'y coupera pas. Les paramètres en entrée de toute procédure liée à un événement souris sont là pour vous permettre notamment de récupérer dans cette procédure :
  • quel bouton de la souris a provoqué l’événement (bouton de gauche, bouton de droite, etc.)
  • l’état des touches Maj, Ctrl et Alt du clavier. Ceci permet de gérer des combinaisons alambiquées clavier + souris. A réserver uniquement aux émules des interfaces Adobe.
  • les coordonnées x et y désignant l’endroit où l’événement s’est produit. Et là, gaffe de chez gaffe ! Ces coordonnées sont toujours relatives à l’objet ayant reçu l'événement. Donc ce ne sont les coordonnées par rapport à la Form que si l’objet recevant l’action est bien la Form. Réciproquement, si on gère un MouseDown sur une image, le X et le Y désigneront la position de la souris par rapport à cette image au moment du MouseDown. Si l'on veut en déduire les coordonnées de la souris par rapport à la Form, il faudra ajouter les coordonnées de l'image dans le Form. Tout le monde suit ?
Quant à l'événement MouseMove, il détecte le déplacement de la souris au-dessus d’un xontrôle. Cet événement possède les mêmes paramètres que les deux événements précédents.
Par ailleurs, il faut signaler en passant qu’en VB, les possibilités de personnaliser l’apparence de la souris sont nombreuses et simples d’utilisation. Chaque contrôle possède ainsi deux propriétés chargées uniquement de gérer cet aspect des choses :
  • MousePointer: indique quelle tête la souris doit prendre lorsqu’elle survole l’objet, tête, choisie parmi la liste des possibilité standard de Windows.
  • MouseIcon désigne un fichier image (type *.ico ou *.cur) définissant un pointeur de souris personnalisé.
On peut donc très facilement, en affectant l'une ou l'autre de ces propriétés (pas les deux en même temps, ça n'aurait aucun sens), faire en sorte que le pointeur de la souris change de tête au survol de tel ou tel contrôle. C'est un des petits détails qui donnent à une interface un look professionnel et "fini", et celui-là, comme on le voit, ne coûte vraiment pas cher.
L'enfer du jeu est un exercice un peu plus long, qui a valeur de récapitulatif. Il comporte deux difficultés majeures.
La première est liée à la formule - et à l'algorithme - de calcul. Cet algorithme est posé sous forme d'exercice, et fort heureusement, de corrigé, dans le fabuleux cours d'algorithmique du même auteur.
La deuxième difficulté tient à l'affichage du résultat sous un format dit "de milliers", format séparant les chiffres par groupe de trois pour améliorer la lisibilité. Cet affichage  n'ayant pas été prévu par le langage VB, la programmation insère des espaces là où il faut, ce qui constitue une splendide prise de tête.
Nom de l'exercice
Exécutable
Sources
Démasqués !
L'enfer du Jeu



4. Gérer le Cliquer - Glisser
Là, on peut vraiment s’amuser. Et par les temps qui courent, il ne faut pas rater de si belles occasions.
Prenons tout de suite un exemple, qui serait de permettre à un utilisateur d’effectuer un cliquer-glisser sur un bouton de commande, afin de le déplacer sur la Form.
Regardons ce qui se passe alors :

Une remarque fondamentale : on doit remarquer que le cliquer-glisser ne provoque en aucun cas de lui-même un déplacement de l’objet de départ. Si l’on veut qu’un tel déplacement survienne, on doit le programmer expressément par le code adéquat.
La gestion du cliquer glisser n’est pas difficile, à condition de suivre méthodiquement les étapes.
  • Etape 1 : il faut autoriser l’objet considéré (ici, le bouton de commande) à subir un cliquer-glisser. Par défaut, l’utilisateur ne peut pas, en effet, cliquer-glisser les contrôles qu’il voit sur une Form. Cette autorisation s’effectue par le passage de l’instruction (qui est une méthode) :
    NomduContrôle.Drag
    Où doit-on écrire cette instruction ? Le plus logique, est d'autoriser l'objet à être cliqué-glissé au moment où l'utilisateur commence la manoeuvre, c'est-à-dire lorsqu'il enfonce le bouton de la souris sur l'objet. Donc, traditionnellement, on placera cette ligne de code au début de la procédure Command1_MouseDown
  • Etape 2 : il faut définir, si on le souhaite, quelle doit être la physionomie du cruseur de la souris durant le cliquer-glisser, autrement dit quelle est "l'icône de glisse" de l'objet. Dans le cas représenté ci-dessus, on s’est contenté de conserver l’icône par défaut pour un bouton de commande. Mais on pourrait tout à fait préciser une autre icône, en affectant la propriété DragIcon de l’objet.
    NomduContrôleGlissé.DragIcon = image
    De même que ci-dessus, cette instruction est elle aussi habituellement placée au moment où se déclenche le Cliquer-Glisser, c'est-à-dire au début de la procédure NomduContrôle_MouseDown.
    Attention, le fichier image doit obligatoirement être de type icône (*.ico), à l’exclusion de tout autre ! Toutefois, plutôt que pointer un fichier contenant l'image voulue, il est toujours plus facile de stocker celle-ci à l'avance dans un contrôle image, en le rendant au besoin invisible, et de pointer ensuite le contenu de ce contrôle Image :
    NomduContrôleGlissé.DragIcon = NomduContrôleImage.Picture
  • Etape 3 : il faut préciser ce qui doit se passer lorsque l’utilisateur va relâcher le bouton de la souris au-dessus de tel ou tel contrôle. Ceci est un événement DragDrop. C'est en particulier là qu'on va passer les lignes de code déplaçant effectivement l'objet, si tel est l'effet recherché.
  • Etape 4 : il faut définir, si on le souhaite, ce qui doit se passer lorsque la souris trimballant l’avatar de l’objet survole tel ou tel contrôle. Ceci se programme par des procédures utilisant l’événement DragOver.
Résumons-nous. Un cliquer-glisser représente donc une série de trois types d’événements potentiels, qu'il va falloir gérer pour chacun des objets susceptibles de les recevoir...

Avec un peu d’habitude et un minimum de rigueur, on peut parvenir à de très jolis résultats.
Dames est un exemple de Drag & Drop où l'on donne l'illusion à l'utilisateur qu'il déplace un objet avec sa souris. Mais rappelez vous, ce n'est qu'une illusion ! Celle-ci est produite par le fait que l'icône de déplacement est la copie conforme de l'objet... Je vous laisse méditer là-dessus. Quant à Manège, il emploie un petit truc algorithmique un brin finaud (mais rien de vraiment méchant).
Nom de l'exercice
Exécutable
Sources
Dames
Manège
L'arpenteur