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) |
|||||||
LE COURS 10. Les graphismes
11. Les menus
AIDES-MÉMOIRES CHOSES DIVERSES Souvent Posées Questions
Liens utiles
|
Partie 7 Événements insolitesIl est temps à présent d'examiner plus en détail différents autres événements que nous permet de gérer C#. Car il faut bien l'avouer, jusqu'ici, à part des clics, des clics et encore des clics, on n'a pas vu grand chose. Or, il serait bien dommage de s'en tenir là, vu les possibilités qu'offre C# en la matière. 1. La notion de FocusEn parlant du Focus, ne pas oublier de prononcer le « s » final, sans quoi cela risque de prêter à confusion. A part ça, le focus, dans une application Windows, désigne le curseur, au sens le plus général du terme. C'est lorsqu'un contrôle possède le « focus » qu'il devient concerné par la frappe d'une touche au clavier (la touche Entrée produisant l'enfoncement d'un bouton, par exemple). Selon les contrôles, le focus se matérialise à l'écran par un curseur clignotant (dans une Textbox), ou par un liseré sombre (sur un Button). Du point de vue de l'utilisateur, il y a deux moyens de déplacer le focus :
L'ordre de passage du focus d'un contrôle à l'autre de la Form est régi par la propriété TabIndex de chaque contrôle : le contrôle qui reçoit le focus par défaut, au lancement de la Form, est celui dont le TabIndex vaut zéro. Il va de soi que C# veille à ce que deux contrôles de la même Form ne puissent jamais posséder la même valeur de TabIndex (il empêche automatiquement qu'il y ait des doublons, ou des trous dans la numérotation). Voyons maintenant le point de vue du programmeur. À tout moment, celui-ci peut placer d'autorité le focus sur un contrôle par du code, en lui appliquant la méthode... Focus. Celle-ci est disponible pour presque tous les contrôles... excepté pour ceux qui ne peuvent recevoir le focus. Étonnant, non ? Mais le code permet également de détecter l'arrivée du focus sur un contrôle, ou son départ. Il suffit pour cela de gérer respectivement les événements Enter et Leave, eux aussi disponibles pour la quasi-totalité des contrôles. 2. Les événements clavier2.1 Une touche, trois événements possibles !Dans un certain nombre d'applications, on peut souhaiter attribuer certaines conséquences à la frappe de certaines touches du clavier. Par exemple, la touche F1 doit ouvrir le fichier d'aide. Autre exemple, vous pilotez en temps réel les mouvements de Zorglubator, le grandiose vaisseau de l'hyperespace, grâce aux touches de direction. Tout cela suppose que la frappe de telle ou telle touche du clavier soit interprétée par le logiciel comme un événement. Aucun problème, Bilou s'occupe de nous, et pour ce faire nous propose trois événements, pas un de moins.
Remarque finaude :
Cela signifie que les touches ne produisant pas de caractères, telles les touches de fonction ou les touches de direction, ne génèrent pas l'événement Keypress. Elles génèrent en revanche les événements KeyDown et KeyUp 2.2 Où l'on parle (enfin) du paramètre eC'est le moment ou jamais de revenir sur un point que nous avions jusqu'à présent laissé de côté : le rôle du second paramètre en entrée des procédures événementielles, le mystérieux e. Tout comme le désormais familier sender, e est une variable qui désigne un objet. Mais cet objet n'est pas le contrôle qui a déclenché la procédure (évidemment, puisque c'est Sender, C# ne va pas mettre deux fois la même information sous deux noms différents, eh, patate). D'ailleurs, l'objet pointé par e n'est pas un contrôle du tout. Il représente, si l'on veut, les conditions, les circonstances, ou les résultats, comme on préfère, de l'événement lui-même. La nature de ses propriétés varie donc d'un événement à l'autre. Remarque « tout se tient » S'il s'agit d'un événement Click, disons-le tout net, il n'y a pour ainsi dire aucune propriété dans e, car rien n'est plus tristement banal et sans caractéristiques particulières qu'un clic. En revanche, s'il s'agit d'un événement clavier, l'objet e est tout de suite beaucoup plus intéressant. Par exemple, lors d'un KeyDown ou d'un KeyUp, e possèdera plusieurs propriétés booléennes (Shift, Alt) ou numériques qui vont nous permettre de connaître en détail l'état du clavier lors du déclenchement de l'événement. La propriété numérique Keycode, par exemple, identifie, sous forme d'un code, quelle touche vient d'être enfoncée ou relâchée. Lors d'un KeyPress, l'objet e possède une propriété de type caractère, KeyChar, qui contient le caractère généré par la touche pressée. Ainsi, le code qui afficherait une à une, dans une MessageBox, les touches frappées au clavier serait :
private void Form1_KeyPress(object sender, KeyPressEventArgs e) Et voilà le travail, enveloppez, c'est pesé. 2.3 Un dernier détail de pure FormIl nous reste toutefois une petite chose à régler avant d'en avoir définitivement terminé avec les événements clavier. Imaginons que nous voulions réaliser un « appel à l'aide » avec la touche F1. Pas le choix, nous devrons passer par un KeyDown (car la touche F1 n'engendrant aucun caractère, elle ne produit donc pas d'événement Keypress). Mais là où ça coince, c'est quand on réfléchit à quel contrôle il faudra affecter l'événement. En effet, lorsqu'on appuiera sur la touche F1, cet événement concernera a priori le contrôle actif (xcelui qui possède le focus). Il faudrait donc, en bonne logique, créer une procédure Chmoll.KeyDown pour chacun des contrôles Chmoll de la Form susceptibles de posséder le focus. On n'est pas rendu. Heureusement, il y a une autre possibilité : demander à la Form la Form de court-circuiter tous les contrôles qui se trouvent sur elle en cas de frappe de touche au clavier. Il suffit pour cela de régler sa propriété KeyPreview (qu'on pourrait traduire approximativement par « interception du clavier ») à True. On n'a plus alors qu'à écrire une seule procédure, celle qui gère l'événement KeyDown sur la Form. Et dans cette procédure, à tester si la touche frappée était bien F1, auquel cas on déclenche l'ouverture de l'aide. Qu'on se rassure, cela n'empêchera nullement, bien sûr, la touche du clavier d'avoir par ailleurs son effet normal, par exemple de venir écrire quelque chose dans une Textbox. Et hop, comme qui rigole. 3. Événements Souris3.1 Évènements peu remarquablesDans la vie d'un informaticien, il n'y a pas que le clavier. Depuis belle lurette, il y a aussi la souris. Et la souris, petit animal vif et malicieux, ça peut faire plein de choses. Ça peut survoler un contrôle. Ça peut se faire appuyer un bouton (ou relâcher un bouton précédemment appuyé) pendant qu'elle est au-dessus d'un contrôle... Bref, une souris, c'est capable d'engendrer une foultitude d'événements aussi intéressants que variés. Trois de ces événements ne requièrent guère de commentaires que cela. Il s'agit de MouseHover et de MouseEnter, qui détectent le passage de la souris sur un contrôle (la nuance entre les deux est si fine qu'elle m'a totalement échappé), et de MouseLeave qui détecte la sortie de la souris d'un contrôle. De ces trois événements, qui effectuent le service minimum, et qui n'envoient quasiment aucun paramètre à la procédure qu'ils déclenchent, il n'y a pas grand chose à dire de plus. En revanche, d'autres événements vont nous permettre, via les propriétés du paramètre e, de récupérer des tas de renseignements utiles. 3.2 Évènements beaucoup plus intéressants
Ces trois événements ont l'intérêt de génerer un objet e comportant plusieurs propriétés tout à fait utiles, parmi lesquelles :
Remarque féline : 3.3 Les folles aventures du curseurDans une interface bien pensée, la souris est un élément essentiel. Dans un sens, elle capte donc des actions de l'utilisateur et peut permettre à l'application d'y réagir en engendrant certains événements. Mais, on l'oublie trop souvent, la souris est aussi un moyen aussi essentiel que simple de communiquer des informations à l'utilisateur, via les changements de forme du curseur. Les applications bureautiques ou graphiques les plus célèbres passent ainsi leur temps à modifier le curseur de la souris pour dire à l'utilisateur que là il peut rétrécir une fenêtre, que là il peut élargir une colonne, que là il peut sélectionner toute une ligne, etc. Manipuler l'apparence du curseur de la souris à bon escient est donc quelque chose qui ne coûte pas cher en termes de savoir-faire technique, et qui est très payante pour l'ergonomie d'une application. Changer le curseur lorsqu'il se balade au-dessus d'un contrôle est extrêmement facile : il suffit de modifier la valeur de la propriété Cursor dudit contrôle. Ainsi, si je passe cette propriété à Cross pour Button1, le curseur de la souris se transformera en croix à chaque survol de Button1 (et se retransformera en flèche normale dès que je survolerai autre chjose ue ce bouton). C'est vraiment simple comme bonjour. Pour demander au curseur de prendre une certaine tête, il y a deux possibilités :
Puisqu'on en est à parler du curseur, je mentionne que celui-ci possède bien d'autres propriétés, à commencer par celle qui définit son emplacement : Position. Cette propriété, tout comme Size ou Location, déjà rencontrées, est une propriété structurée – elle est formée de deux entiers qui désignent l'abcisse et l'ordonnée du curseur, sachant que l'origine du repère se situe au coin supérieur gauche de l'écran. Position peut être utilisée, au choix, en lecture (pour récupérer la position actuelle du curseur) ou en écriture (pour la modifier). En lecture, le plus simple est d'utiliser le fait que cette propriété se décompose en deux sous-propriétés, X et Y. Pour récupérer l'emplacement du curseur dans l'écran, on pourra ainsi écrire : int abscisseCurseur = Cursor.Position.X En écriture, on devra savoir que le nom de la structure sur laquelle est bâtie la propriété Position n'est pas, comme on pourrait s'y attendre logiquement, « Position », mais... Point. Une fois ce petit détail réglé, ça va tout seul, par exemple pour placer autoritairement le curseur dans le coin supérieur gauche de l'écran : Cursor.Position = new Point(0, 0); Allez, distrayons-nous un peu :
3. Le Glisser - Déposer (Drag & Drop)Un des trucs balaises dans l'interface graphique de Windows, c'est qu'on peut se servir de sa souris pour prendre des trucs à un endroit, les trimballer et aller les mettre ailleurs. La quasi-totalité des logiciels exploitent cette possibilité, et il serait quand bien même bien dommage que nous n'apprenions pas à programmer avec C# ce qu'on appelle en français le « Glisser - Déposer », et en anglais le « Drag and Drop ». N'est-il pas ? Cela dit, mieux vaut le savoir, mettre en oeuvre le Drag and Drop, cela suppose une fieffée dose de patience et de rigueur, car le moins qu'on puisse dire, c'est que ça ne glisse pas comme sur des roulettes et qu'à la fin, c'est souvent les armes qu'on dépose. Mais bon, en y allant le plus rationnellement et le plus méthodiquement possible, on peut espérer s'en sortir vivants. 3.1 Approche générale3.1.1 Les préparatifsUn Drag & Drop est un traitement un chouia complexe, composé d'un certain nombre d'instructions et de procédures évènementielles obligatoires. Et naturellement, on peut enrichir tout cela par des choses plus facultatives. Comme à chaque jour suffit sa peine, commençons par le strict nécessaire. Un Drag & Drop implique toujours au moins deux contrôles :
Remarque de bonne méthode : Poursuivons. Pour qu'il puisse se passer quoi que ce soit, deux conditions indispensables doivent être présentes.
3.1.2 Le décollageÀ ce stade de la compétition, tout est prêt. Les rampes de lancement sont dressées, les moteurs allumés. Il n'y a plus qu'à effectuer la mise à feu. Celle-ci, le déclenchement du Drag & Drop proprement dit, s'effectue via le passage de la méthode DoDragDrop à ContrôleD. Le truc, c'est que cette méthode réclame deux arguments pas évidents à comprendre par la simple intuition :
Tout cela donnea donc une ligne pouvant par exemple ressembler à : ControleD.DoDragDrop("AstalavistaBaby", DragDropEffects.All)
Remarque de mise à feu : 3.1.3 Le survolÀ partir du moment où l'instruction DoDragDrop est exécutée, le Drag & Drop proprement dit est enclenché. On pourrait (naïvement) penser que celui-ci se limite ensuite à gérer le Drop, c'est-à-dire l'atterrissage. Mais nenni. C# nous impose de gérer un évènement supplémentaire, sans lequel rien ne fonctionnera. C'est d'autant plus déroutant que cet évènement semble ne pas servir à grand chose, mais bon, pas le choix, il faut faire, on fait. Cet évènement en question est celui qui correspond à l'arrivée du curseur au-dessus du contrôle cible, autrement dit ControleC.DragEnter. La procédure qui lui correspond devra comporter une instruction, et une seule, consistant à affectuer la propriété Effect de l'objet e, par un membre de l'émnumération déjà rencontrée concernant les images de curseur. C'est un peu comme si on disait à la machine : « Attention, un engin volant non identifié est en approche. Nous lui donnons l'autorisation d'atterrir. » Cela donnera, par exemple : e.Effect = DragDropEffects.All Et toc, un problème de plus en moins. 3.1.4 L'atterrissageOn en arrive à l'ultime étape : le Drop proprement dit. Ceci ne pose aucune difficulté : il suffit de gérer l'évènement adéquat, soit ControleC.DragDrop. C'est dans cette procédure qu'on pourra donner toutes les instructions permettant d'obtenir les effets désirés.
Remarque résumeuse : 3.2 Programmer les effets du Drag & DropJusqu'ici, nous avons confectionné un superbe écrin... vide. En effet, nous avons vu dans le détail comment programmer l'ensemble des évènements d'un Drag & Drop, mais finalement, cette belle série de cabrioles ne mène (pour l'instant) à rien du tout. On prend un contrôle (ou un élément de contrôle), on le trimbale, on relâche le bouton, et là... rien. Maintenant qu'on a le fusil, il est donc grand temps de mettre la balle dedans. 3.2.1 La méthode officielle (déconseillée)Théoriquement, l'effet le plus courant d'un Drag & Drop, à savoir le transport d'une information depuis le contrôle D vers le contrôle C, est censé être pris en charge par un des objets qui sont générés par les évènements du Drag & Drop, à savoir le fameux paramètre e ; et plus précisément, par sa propriété Data. Toute l'affaire se déroule en deux temps. Et d'une, au démarrage du Drag and Drop, c'est-à-dire lors du passage de la méthode DoDragDrop, l'information voulue est passée à e.Data : c'est le premier des deux arguments de cette méthode, celui pour lequel on a rentré tout à l'heure la première chose qui nous passait par la tête, à savoir "AstalavistaBaby". Mais en théorie, nous étions censés écrire ici une valeur plus significative et utile pour notre problème, comme le nom du contrôle, la valeur de l'une de ses propriétés, un de ses items si c'est une liste, etc. Et de deux, le plus rigolo. Lors de l'atterrissage, c'est à dire dans la procédure DragDrop, on peut récupérer l'information qui se trouve dans la propriété e.Data et ainsi s'en servir. Sauf que... sauf que, cette récupération pose des problèmes de syntaxe à ingurgiter des aspirines par seaux entiers. Pour commencer, quel que soit la nature de son contenu, pas question d'accéder directement à l'information recherchée. Il va impérativement falloir recourir à la méthode GetData. Ce qui donnera donc un très joli : e.Data.GetData Mais les ennuis ne font que commencer. Si les informations qu'on a rangées dans cette propriété ont le bon goût d'être d'une nature simple (du texte), il faudra préciser de surcroît le format dans lequel on veut récupérer l'information, par le très joli DataFormats.Text... sans omettre de préciser que C# doit convertir le tout en type String ! On aboutit donc à l'usine à gaz suivante : e.Data.GetData(DataFormats.Text).ToString Tout ça pour une malheureuse donnée de type texte ! Comme disait (à peu près) Obélix : « Ils sont fous chez Redmond ! » Car, est-il besoin de le préciser, dès que la donnée stockée dans e est d'un type plus complexe (structure, image...) la récupérer dans le e.Data va devenir une vraie torture, où l'on pourra passer des heures à chercher (en vain) la syntaxe adéquate. Voilà pourquoi, très franchement, je ne vois aucun avantage à utiliser les voies officielles, et je conseille vivement d'opérer en contrebande. 3.2.2 La technique de contrebande (de loin la plus simple)Cette technique va consister à éviter comme la peste l'objet e, et à véhiculer tout simplement la ou les informations voulues... par des variables ordinaires. Étant donné les circonstances, il faudra veiller à deux choses :
3.3 Brancher les événements par du codeLa dernière chose à dire, c'est que tous les événements souris, qu'il s'agisse ou non de drag & drop, demandent une instruction un peu spécifique pour pouvoir être programmés par du code. Il n'est en effet pas possible d'utiliser, comme nous l'avons fait jusqu'à présent, l'instruction EventHandler pour gérer ce type d'évenements. En l'occurrence, on devra donc passer par deux variantes :
Remarque astuce de la mort qui tue : Le premier exercice, Boîte à Meuh, est une simple petite mise en jambes :
aucune difficulté, aucun piège. | ||||||