Composant non graphique qui détecte sur quelle form il a été posé

Soyez le premier à donner votre avis sur cette source.

Snippet vu 11 961 fois - Téléchargée 29 fois

Contenu du snippet

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é.

Source / Exemple :


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using System.ComponentModel.Design;

namespace FreshNotes
{
    public class ChangeCouleur : Component
    {
        private Form _ParentForm; // Page sur laquelle a été déposée le contrôle

        /// <summary>
        /// Permet de définir/récupérer le contrôle parent
        /// </summary>
        [Browsable(false)]
        public Form ParentForm
        {
            // Accesseur en lecture
            get
            {
                // Si l'on est en mode design, alors on demande au designer de définir la propriété ParentForm avec
                // le this de la form contenant l'instance du composant.
                if (this.Site.DesignMode)
                {
                    IDesignerHost dh = (IDesignerHost)(this.GetService(typeof(IDesignerHost)));
                    if (dh != null)
                    {
                        object obj = dh.RootComponent;
                        if (obj != null)
                        {
                            _ParentForm = (Form)obj;
                        }
                    }
                }
                return _ParentForm;
            }

            // Accesseur en écriture
            set
            {
                if (value != null) _ParentForm = value;
            }
        }

        /// <summary>
        /// Ma propriété couleur
        /// </summary>
        public Color Couleur
        {
            set {
                if (_ParentForm == null) return;
                _ParentForm.BackColor = value;
            }
            get { return _ParentForm.BackColor; }
        }

        /// <summary>
        /// Constructeur
        /// </summary>
        public ChangeCouleur()
        {
        }
    }
}

Conclusion :


Pour info, je n'ai pas trouvé cette astuce tout seul, mais j'ai trouvé un excélent article qui décrit la méthode en détail :
http://www.code-magazine.com/articleprint.aspx?quickid=0401091&printmode=true

Le gars (Ken Getz), a réussi à trouver l'astuce en décompilant le composant Timer.

On sent quand même ici une petite lacune du framework (le 1 comme le 2), et j'ai l'impression que même les mecs de Microsoft qui ont développé le Timer ont dû contourner cette lacune :D

NOTE IMPORTANTE : Merci de ne pas faire de commentaire du genre, je n'ai pas besoin de faire un composant qui modifie la couleur de ma form, je peux très bien manipuler le BackColor de ma form directement. Oui, OK, je suis d'accord avec ça, mais il n'empèche, dans certains cas, cette méthode s'avère utile. Par exemple, en ce moment, j'essaye de développer un composant non graphique qui ajoute un bouton Tray dans la barre de titre de la form sur laquelle il est posé. Et bien mon composant à besoin du ParentForm, et c'est ici que mon petit bout de code peut m'être utile ;-) Autre info, le dit composant ne peut pas dériver de UserControl, puisque je n'ajoute pas de contenu graphique dans le corps de la fenêtre mais plutôt dans la barre de titre, ce qui est totallement différent, puisqu'un UserControl ne peut pas s'y loger.

A voir également

Ajouter un commentaire

Commentaires

cs_yoannd
Messages postés
305
Date d'inscription
lundi 7 janvier 2002
Statut
Membre
Dernière intervention
10 août 2011
4
Salut !

Effectivement, ce test manquait. Même si l'idée de base était de récupérer la form parente, une petite correction de bug ne se refuse jamais !

a+
harpoceras
Messages postés
1
Date d'inscription
mercredi 30 novembre 2005
Statut
Membre
Dernière intervention
26 octobre 2006

Très utile.
Le problème, c'est qu'en reprenant ce code tel quel, ça marche en design mais j'ai un plantage à l'exécution.
Je remarque que le designer génère ces lignes :
//
// changeCouleur1
//
this.changeCouleur1.Couleur = System.Drawing.Color.Snow;
this.changeCouleur1.ParentForm = this;

Apparemment, l'ordre n'est pas bon. Si j'intervertis les lignes à la main, ça marche (affectation de la couleur après affectation de la form parente).
Dans la classe, j'ai alors modifié la gestion de la propriété Couleur en ajoutant une ligne :

public Color Couleur
{
set
{
if (_ParentForm == null) return;
_ParentForm.BackColor = value;
}
get
{
return _ParentForm.BackColor;
}
}

Là, ça marche bien.

J'ajoute que je suis novice et qu'il y a peut-être qq chose que je n'ai pas compris...
cs_yoannd
Messages postés
305
Date d'inscription
lundi 7 janvier 2002
Statut
Membre
Dernière intervention
10 août 2011
4
Salut !

Je vous remercie pour vos commentaires. Pour ce qui est du composant qui ajoute un bouton dans la barre de titre, tu peux aller ici :
http://www.csharpfr.com/code.aspx?ID=34918

Attention, le composant n'est pas fini, mais il peut te donner un apperçu de ce que je recherche.
cs_badrbadr
Messages postés
475
Date d'inscription
jeudi 19 juin 2003
Statut
Membre
Dernière intervention
3 novembre 2008

c cool,
c est pas inutile quand on prend le temps d y penser meme si au debut, quand j ai commencé ma lecture, je me disais: a quoi ca sert? (désolé :)
mais quand j'ai terminé, c'est interessant

en passant, j aimerais bien que tu m envoies ton programme qui ajoute un bouton dans la barre titre, ca m'interesse (tous les trucs graphiques speciales m interessent).
utilise le systeme code source pour m envoyer un message :) merci d avance

et salut
sebmafate
Messages postés
4936
Date d'inscription
lundi 17 février 2003
Statut
Modérateur
Dernière intervention
14 février 2014
32
Bien... explications claires... code commenté... rien à redire ;)

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.