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ôlesNous 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 Form1.1 PropriétésLa 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 C# 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 C# :
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. this.Size = new Size(500,
300);
Heureusement, il y a un moyen de contourner l'obstacle et de se simplifier considérablement 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 elles sont de simples Integer. Ainsi, la ligne ci-dessus pourra avantageusement être remplacée par : this.Width =
500;
this.Height = 300; ...ce qui est tout de même beaucoup plus simple. 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. Remarque remarquable :
La form est le seul contrôle qui, dans le code, n'est pas appelé par son nom ? parce que c'est le seul contrôle qui se désigne, en quelque sorte, depuis l'intérieur de lui-même (aïe, ma tête !). Une forme parlera donc d'elle-même en utilisant le mot réservé this. this.Location
= new Point(0,
0);
...on préfèrera le beaucoup plus intuitif : this.Top = 0;
this.Left = 0; Conclusion : 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 finir, 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 :
1.2 ÉvénementsLes Form sont capables de gérer nombre d'événements. 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. Conclusion logique, 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 ; elle devient par la même occasion la Form active, et elle le reste nécessairement 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 différents é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 ButtonLes 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 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 : le 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 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 exception 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 cette page de l'aide-mémoire éclairera votre lanterne. 3. Une parenthèse nécessaire : le paramètre sender3.1 PrésentationNous interrompons à présent le cours normal de nos émissions pour un flash spécial 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'expéditeur »). 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 justement 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 void
Truc(object sender, EventArgs
e)
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, vient d'être cliqué. Eh bien, c'est à cela que sert sender. On remarque que ce paramètre est défini par un drôle de type : object. Autrement dit, sender est une variable objet, c'est-à-dire une une variable qui pointe sur un objet ? celui qui a déclenché l'exécution de la procédure. Ici, selon les cas, sender vaudra donc (c'est-à-dire : fera référence à) Bouton1 ou Bouton2. Toute l'affaire va donc consister à utiliser cette variable à l'intérieur de la procédure, soit en lecture (pour récupérer une des propriétés de l'objet, par exemple son Text) soit même en écriture (pour modifier une ou plusieurs propriétés de l'objet pointé). 3.2 Utilisation en lecturePetit souci : sender étant une variable de type object, c'est-à-dire un type extrêmement général, elle ne possède aucune des propriétés de l'objet précis vers lequel elle pointe. Dans notre exemple, alors que nos boutons ont un top, un left, un width, un height, un text, etc., sender ne possède rien de tout cela. Heureusement, il existe une solution simple : recopier, en le convertissant, l'objet désigné par sender vers une variable du bon type, en l'occurrence Button. Les geek du C# ont un terme de jargon franglais pour désigner une telle recopie / conversion d'une variable vers un autre type : ils appellent cela « caster » une variable. Naturellement, pour que l'affaire se déroule sans erreur, il faut impérativement que le type de l'objet désigné par sender soit bel et bien le type vers lequel on le caste - sinon, cela revient à vouloir faire rentrer le gros machin dans le petit bidule, et ça va coincer... Pour un exposé systématique des affaires de cast, je vous renvoie vers cette page de l'aide-mémoire. Pour l'heure, il nous suffira de savoir que le cast va se faire en mettant entre parenthèses le type voulu devant la valeur concernée. Ici, pour créer une variable MonBouton du type Button et y recopier sender, on écrira : Button
MonBouton = (Button)sender;
À partir de là, la variable MonBouton, ayant été déclarée comme une variable pointant sur un objet de la classe Button, donne accès à toutes les propriétés et les méthodes de cette classe. On pourra donc accéder, en quelque sorte par ricochet, aux propriétés de Bouton1 et de Bouton2. Par exemple, pour écrire dans la barre de titre de l'application à quelle distance du haut de la Form se trouve le bouton sur lequel on vient de cliquer : this.Text
= "Le bouton cliqué est à "
+ MonBouton.Top + " pixels du
bord supérieur";
Et le tour est joué ! 3.3 Utilisation en écritureDe la même manière, on peut sans aucun souci utiliser le même mécanisme en écriture, pour modifier une propriété de l'objet désigné par la variable sender ; ainsi, par exemple, pour écrire quelque chose sur le bouton sur lequel on vient de cliquer : Button
MonBouton = (Button)sender;
MonBouton.Text = "Argh. Vous m'avez cliqué dessus."; Simple comme bonjour, n'est-il pas ? « Pourtant, ça ne devrait pas marcher ! » : voilà ce qu'on est en droit de se dire en regardant les lignes qui précèdent. En effet, en l'absence de toute précision, on doit supposer que sender est un paramètre passé par valeur à la procédure. En plus, on ne modifie même pas une propriété de sender, mais une propriété de MonBouton, qui est lui-même une copie de sender (donc, une copie d'une copie du bouton initial). En toute logique, ces instructions ne devraient pas avoir le moindre effet sur Bouton1 ou Bouton 2 ! Et pourtant, si... Le truc à savoir, et qui explique tout, c'est que les variables de type object, ainsi que tous les sous-types qui s'y rattachent, comme Button, sont par définition des pointeurs. Elles ne contiennent pas l'objet, mais une référence à cet objet. Du coup, sender n'est pas passé par référence, mais il est lui-même une référence ; et modifier une propriété de sender (si c'était possible) ce serait modifier une propriété de l'objet auquel sender fait référence. De même, quand on recopie sender dans une variable MonBouton, on ne recopie pas un objet, mais une référence à un objet (celui auquel sender fait référence ? vous suivez toujours ?). Donc, en modifiant une propriété de MonBouton, par un furieux coup de billard à trois bandes, on modifie bel et bien une propriété de Bouton1 ou de Bouton2. Comme quoi, la programmation, c'et bel et bien de la logique et non de la magie noire (même si, parfois, ça peut y ressembler). Vous pouvez à présent reprendre Indiana
Jones en faisant en sorte de simplifier le code par
l'utilisation de sender.
4. La classe Label4.1 Le Label ordinairePoursuivons la découverte des classes proposées par C#, en faisant la connaissance de la classe « étiquette », autrement dit Label. Commençons par évacuer deux considérations essentielles :
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 à un simple texte inerte, posé 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 en aucun cas 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, entre autres, trois propriétés intéressantes de cette classe :
Et voilà tout ce qu'il y a d'important à dire sur la classe Label pour le moment. 4.1 La classe LinkLabelIl 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 en 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 :
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 pas de
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 aborder la programmation C# autrement qu'en jetant ce qui vous vient à l'esprit au petit bonheur, tout en caressant l'écran avec un trèfle à quatre feuilles en espérant que ça finisse par marcher. |