Pour ceux qui ont déjà essayé de créer des composants ergonomiques, vous savez certainement qu'il en existe de plusieurs types :
- Les graphiques, qui ajoutent un contenu visuel à l'interface utilisateur, et qui dérivent de la classe UserControl. Par exemple, un bouton fait partie de cette catégorie de composants.
- Les non graphiques, qui ajoutent des fonctionnalités à votre logiciel, et qui la plus part du temps n'affichent rien de spécial. Le timer en est un bon exemple. Le NotifyIcon fait aussi partie de cette catégorie, et bien qu'il permette l'affichage d'une icône à côté de l'heure, il n'ajoute pas de contenu visuel à votre page.
- Et d'autres encore, comme les controles conteneurs, ...
Partons du principe que je veuille faire un composant qui, lorsque je modifie sa propriété "Couleur", modifie la propriété BackColor de ma form. Nous sommes d'accord : Le composant n'affiche rien de spécial dans la form, mais met simplement à jour une propriété de la form en elle même. Le composant a besoin de savoir sur qu'elle form il doit travailler, mais je n'ai pas envie de le spécifier à celui-ci quand je l'utilise : je considère que déposer un composant sur une form est assez explicite pour lui dire sur quelle form travailler.
En créant un UserControl, il est facile de récupérer la form parent. Ceci ce fait par un simple this.ParentForm. OR, dans le cas présent, nous voulons dériver de la classe System.ComponentModel.Component, puisque le composant n'est pas graphique. "this.ParentForm" n'est alors plus accessible ! Comment faire pour savoir sur quelle form a été posée notre contrôle ? Et bien pour répondre à cette question, je vous propose de regarder le code fourni ici.
Explications :
Tout se joue au moment du design ! Rien n'est détecté au moment de l'exécution !!! En fait, lorsque je dépose le composant que j'ai créé sur une form, le designer tente de récupérer toutes les valeurs des propriétés de mon composant (en tout cas celles dont une variable par défaut n'est pas définie, mais c'est une autre histoire), puis génère des lignes de code dans le InitializeComponent. Les lignes de code générées sont de type :
[NomDuComposant].[Propriété] = [ValeurRetournéeParLeGetDeLaPropriété];
Dans mon get, je vais alors chercher au moyen du DesignerHost (qui fournit un point d'accès au designer) un truc qui s'appèle le RootComponent. Je ne fais ceci que lorsque je suis en mode design. Ainsi, lorsque je pose mon composant ChangeCouleur (c'est con nom) sur ma form, le designer ajoute la ligne de code suivante dans le InitializeComponent :
//
// changeCouleur1
//
this.changeCouleur1.ParentForm = this;
Et voilà, c'est gagné ! La solution est plus que chelou, mais c'est celle qui est utilisée par exemple pour le timer, qui a semble-t-il besoin d'un handle vers la fenêtre sur laquelle il a été posé.