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 12
Travailler avec plusieurs FormJusqu'à maintenant, nous n'avons jamais programmé d'application qui utilise plusieurs Form à la fois. Or, il faut quand même connaître quelques éléments à ce propos, car dès que les programmes grossissent, ils ont beaucoup de mal à tenir sur une Form unique. Il y a deux grandes manières d'utiliser plusieurs form dans une application :
Si la première architecture n'est pas très fréquente, on aura reconnu dans la seconde l'organisation classique de toutes les applications professionnelles de bureautique, où la fenêtre principale comprend les menus qui servent à gérer les différents documents (au besoin, plusieurs en même temps), qui seront ouverts. 1. Des form sans hiérarchie1.1 Activer une Form depuis une autre FormLe premier problème qui se pose est de comprendre le fonctionnement des activations de Form ‐ je rapelle qu'on se situe ici hors du cas où une Form principale contient des Form enfants. Nous avons donc conçu une série de Form, que j'appellerai par leur Name, en l'occurrence et par fainéantise : Form1, Form2, Form3, etc. Et je suppose donc qu'au cours de l'application, telle action sur un des contrôles de Form1 devra déclencher le passage à Form2, telle action sur un des contrôles de Form2 le passage à Form3, etc. Nous savons déjà demander à la machine d'afficher une form et de la rendre active : il suffit pour cela d'employer la méthode Show (comme nous l'avons fait maintes fois avec les MessageBoxDialog). Oui mais voilà : il y a une petite subtilité supplémentaire. Impossible en effet d'écrire simplement : ![]() Form2.Show(); ...car la méthode n'est pas disponible pour la classe Form. Le truc, c'est qu'il faut commencer par instancier la Form avant de pouvoir se servir du résultat de cette instanciation. En fait, c'est assez logique, quand on y réfléchit un peu. Ce que nous avons fabriqué sous le nom de Form2 en mode design, ce n'est pas (encore) un objet ; c'est une classe-fille de la classe Form en général. C'est pour cela que le code associé à toute Form (par exemple, Form1) comprend cette ligne : public partial class Form1 : Form
Ce qui signifie : « crée une classe du nom de Form1, qui héritera de la classe Form ». Revenons donc à notre problème. Nous ne pouvons pas directement rendre active Form2, mais nous pouvons rendre active une instance de Form2. Il nous faut donc créer cette instance, pour l'activer ensuite :
var truc = new Form2();
truc.Show(); Et là, pas de problème, ça passe comme une lettre à la poste. Au passage, profitons-en pour apprendre qu'une Form est dite modale si elle est obligatoirement la Form active tant qu'elle est présente (autrement dit si, tant qu'elle est là, on ne peut rendre aucune autre Form active). La méthode que nous avons employée affiche Form2 de manière non modale : l'utilisateur peut librement réactiver la Form de départ s'il le souhaite. Pour que Form2 soit modale, il suffit d'employer, en lieu et place de la méthode Show, la méthode ShowDialog. 1.2 Événements liés aux Form (rappel)Au lancement de l'application, l'ensemble des Form est chargé en mémoire. Pour chacune de ces Form, l'évènement Load est donc déclenché, et si l'on a écrit une procédure liée à cette évémenent, elle est exécutée (a priori, une fois pour toutes). Dans le cas où l'application emploie plusieurs Form, il faut donc bien faire la différence avec l'évement Activate : celui-ci se déclenchera à chaque fois qu'une Form sera rendue active, soit par l'exécution d'une méthode Show, soit parce qu'elle était disponible à l'écran et que l'utilisateur cliquera sur elle. 1.3 Transmettre des informations d'une Form à l'autreVoilà un petit problème tout bête qui peut nous entraîner très loin... Voilà pourquoi, dans ce cours, on se contentera de quelques indications. 1.3.1 Passer par des variables globalesLa méthode la plus stupide (mais la plus simple, et la plus efficace) consiste à ne pas s'embêter. Si nous avons besoin d'informations communes à toutes les Form de notre application, nous stockerons ces informations dans des variables accessibles depuis n'importe quelle Form. L'avantage de cette méthode est la simplicité de sa mise en œuvre. Pour commencer, les variables seront déclarées à la fois publiques et statiques. Publiques, parce qu'elles devront être visibles depuis n'importe quelle Form. Statiques, parce qu'elles devront conserver leur valeur indépendamment des différents appels de procédures. Une telle déclaration s'effectuera de la manière suivante : public static int toto = 0; Mais il ne suffit pas de déclarer la variable ; encore faut-il la déclarer au bon endroit. En l'occurrence, tout dépend de la manière dont est organisée notre application et dont les différentes form se succèdent au cours de l'exécution.
1.3.2 Manipuler directement les propriétés des contrôles externesUn problème se pose lorsqu'on veut pouvoir agir sur un contrôle situé sur une form depuis une procédure située sur une autre form. Par défaut, les contrôles sont « privés », c'est-à-dire que leur propriété Modifiers vaut Private. Pour que le contrôle devienne accessible depuis n'importe quelle Form du projet, il suffit de passer cette propriété à Public. Ainsi, si l'on veut écrire « coucou » sur le bouton monBouton de Form2, on aura (après avoir mis la bonne valeur pour la propriété Modifiers de monBouton) quelque chose du genre :
var maForm = new Form2();
maForm.monBouton.Text = "coucou"; Cette technique possède l'avantage de la simplicité. Elle souffre néanmoins de deux inconvénients.
1.3.3 Une technique plus sécuriséePour modifier des contrôles sur une form extérieure, plutôt que rendre les contrôles publics, on peut préférer utiliser le passage de paramètres (donc d'informations) via le constructeur, lorsqu'on instancie cette form. Reprenons l'exemple de ma classe Form2, sur laquelle je souhaite pouvoir tripoter depuis Form1 le texte du bouton monBouton. Normalement, par défaut, le constructeur de Form2 (visible sur le code attaché à cette classe), possède la physionomie suivante : public Form2() Pour lui transmettre une (ou plusieurs) informations, il suffit d'introduire un paramètre (ou plusieurs) dans cette procédure, et de s'en servir pour modifier une (ou plusieurs) propriété(s) d'objets de la Form : public Form2(string truc) Ce qui signifie : « lors de toute instanciation, on doit te fournir en paramètre un texte, qui deviendra illico ce qui est écrit sur le bouton monBouton. Dès lors, pour obtenir le résultat voulu, il suffira, depuis n'importe quelle Form, d'instancier Form2 avec ledit paramètre :
var maForm = new Form2("coucou");
2. La structure hiérarchique (une mère, des filles)Pour finir ce cours en beauté, quelques mots des applications poétiquement dites "MDI", à savoir : Multiple Document Interface. Il s'agit du lot commun des applications Windows bien connues, du genre Word, Excel, Photoshop et tutti quanti. Toutes ces applications, malgré leurs différences, ont en commun une interface gérée par Windows, dans laquelle :
La réalisation d'une application MDI en C# ne pose pas de difficultés exagérées, à condition, comme toujours, de procéder méthodiquement. 2.1 En mode designLa première chose à faire est de définir la Form principale (parente). Ceci s'effectue très simplement, en donnant la valeur True à sa propriété IsMdiContainer. Visuellement, le designer indique alors qu'il s'agit d'une form parente en rendant son fond gris foncé. A priori, la form parente, dans une application, est unique. On ne peut cependant pas exlure qu'il en existe plusieurs : mais là, ça devient quand même une interface un tantinet tordue. Les form enfants, elles, ne nécessitent pas de manipulation particulière : il faut simplement qu'il en existe une par type de Form enfant voulu, sachant que les différents exemplaires d'un même type seront instanciés dynamiquement par du code. Par exemple, si l'on veut créer un traitement de textes, on aura une form enfant (qui contiendra du texte) et qui, lors de l'exécution, sera dupliquée en autant d'exemplaires qu'il y aura de documents différents à gérer en même temps. 2.2 Instanciation des Form enfantsAu cours de l'application, les différentes Form enfants seront toutes, par définition, des instanciations de celle(s) qu'on aura créées en mode design. Pour le dire autrement, en mode design, on fabrique le moule : il s'agit à présent d'écrire le code nécessaire pour cuisiner les gâteaux. Sans surprise, ce code obéit aux règles générales de la création dynamique de contrôles. La seule nouveauté consiste à signifier que la nouvelle Form est une Form enfant de telle Form parent. Ceci s'effectue via la propriété MdiParent de la Form enfant, une propriété accessible seulement par le code. Ainsi, en admettant que nous ayons baptisé monGateau la Form servant de modèle à nos Form enfants, voici le code qui crée une nouvelle instance de cette Form à partir d'un contrôle ou d'un menu de la Form principale :
Form monGateau = new Enfant(); 2.3 Maniement des Form enfantsCe point pose problème uniquement lorsqu'on doit manier les Form enfant à partir de la Form parent. Le maniement des Form enfants à partir d'elles-mêmes se fait tout naturellement : programmer une Form à partir d'elle-même, c'est très précisément ce que nous avons fait depuis le début de ce cours. La seule difficulté consiste à savoir comment désigner une (ou plusieurs) form enfants à partir de la form principale. Pour cela, le moyen le plus simple consiste à partir du fait que cette dernière définit une collection de Form enfants : this.MdiChildren. On pourra ainsi balayer l'ensemble des Form enfant par une boucle parcourant cette collection, ou rechercher une Form particulière au sein de cette collection en fonction de l'une ou l'autre de ses propriétés. On notera pour terminer que la Form parent possède la propriété ActiveMdiChild, qui renvoie la Form enfant actuellement active.
|