Il m'arrive regulierement de creer des composant perso en .NET.
Le probleme auquel je suis souvent confronte est le suivant...
Il n'est pas rare qu'un composant personnel herite d'un composant tel qu'un Button ou un autre composant comportant multes proprietes.
Dans certains cas, le fait de modifier les proprietes du composant dont on herite peut deranger le bon fonctionnement du composant personnel car on n'a pas juge "necessaire" de surcharger telle ou telle methode.
Si on est le seul a utiliser le composant pas de probleme, mais si c'est une librairie utilisee au sein d'une equipe de dev, bonjour les degats.
Tout le monde a ses petites habitudes, "je veux que mon bouton soit en Flat", "Je veux un font Segoe UI..."
Afin de contrer ce probleme, on peut choisir quelles proprietes vont etre visibles via le PropertyGrid de l'IDE grace a l'interface ICustomTypeDescriptor (entre autre).
Evidemment les proprietes restent accessibles via le code, ce n'est donc pas une solution miracle, mais au moins les dev peuvent avoir une visu sur ce qu'ils peuvent utiliser via la PropertyGrid du designer. S'ils s'amusent apres a jouer avec le composant dans le code, c'est leur probleme.
J'ai remarque que ce sujet etait peu voire pas du tout documente en francais, ou en tout cas les rares existants restent tres vagues et decrivent l'interface ICustomTypeDescriptor en general et n'abordent donc pas ce point precis. Je tente donc de m'y coller du mieux que je peux.
Voici donc un composant "MyButton" qui herite de "Button"
namespace
MyFuckinButton
{
[ToolboxItem(true),
ToolboxBitmap(typeof(Button)),
Description("Un banal
bouton")]
public class MyButton : Button, ICustomTypeDescriptor
{
#region Variables
/// <summary>
/// _ pdc: collection des
proprietes qui va etre consultee
/// par le desginer property grid (entre autre, mais dans le
/// cas present c'est ce qui nous interesse)
/// </summary>
private PropertyDescriptorCollection
_pdc = null;
#endregion
// Constructor
public MyButton()
{
InitializeComponent();
}
#region Properties
/// <summary>
/// ButtonText: Propriete
ultra powerful rattachee a base.Text
/// </summary>
[Category("MyOnlyButtonProperty")]
public string
ButtonText
{
get { return base.Text; }
set { base.Text = value; }
}
#endregion
#region ICustomTypeDescriptor
dependencies
/// Rien de magique pour le moment
/// Implementation typique des methodes de l'interface
ICustomTypeDescriptor
public TypeConverter GetConverter()
{
return TypeDescriptor.GetConverter(this, true);
}
public EventDescriptorCollection
GetEvents(Attribute[] attributes)
{
return TypeDescriptor.GetEvents(this, attributes, true);
}
EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents()
{
return TypeDescriptor.GetEvents(this, true);
}
public string
GetComponentName()
{
return TypeDescriptor.GetComponentName(this, true);
}
public object
GetPropertyOwner(PropertyDescriptor pd)
{
return this;
}
public AttributeCollection
GetAttributes()
{
return TypeDescriptor.GetAttributes(this, true);
}
public PropertyDescriptorCollection
GetProperties(Attribute[] attributes)
{
return GetProperties();
}
PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties()
{
return TypeDescriptor.GetProperties(this, true);
}
public object
GetEditor(Type editorBaseType)
{
return TypeDescriptor.GetEditor(this, editorBaseType, true);
}
public PropertyDescriptor
GetDefaultProperty()
{
return TypeDescriptor.GetDefaultProperty(this, true);
}
public EventDescriptor
GetDefaultEvent()
{
return TypeDescriptor.GetDefaultEvent(this, true);
}
public string
GetClassName()
{
return TypeDescriptor.GetClassName(this, true);
}
// C'est ici
que tout se joue
// C'est en
effet cette methode qui est appelee afin de connaitre
// les
proprietes associees a notre classe
public PropertyDescriptorCollection GetProperties()
{
// On ne
va evidemment le generer qu'a la premiere demande
if
(_pdc == null)
{
// On
recupere la collection des proprietes reelles de notre classe
PropertyDescriptorCollection defaultProp = TypeDescriptor.GetProperties(this, true);
_pdc = new PropertyDescriptorCollection(null);
//
Pour chaque Propriete contenu dans defaultProp
foreach (PropertyDescriptor pd in
defaultProp)
{
//
A cet endroit la on va pouvoir ajouter les proprietes voulues
//
a notre nouvelle collection pdc
//
Dans cet exemple je choisis de n'afficher que les proprietes
//
de ma classe courante et de ne pas ajouter celle qui sont presentes
//
a cause de la classe dont on herite
if (TypeDescriptor.GetProperties(typeof(Button)).Contains(pd) == false)
{
// Si je suis ici c'est que la propriete ne vient pas de la classe
// J'ajoute donc l'element actuel a la collection
_pdc.Add(pd);
}
}
}
return
_pdc;
}
#endregion
#region Designer dependencies
// Rien de
special, juste les choses "necessaires"
// que j'ai
choisi d'inclure aussi dans ce fichier pour que tout soit regroupe
// afin de
pouvoir diffuser la classe en entier
private
System.ComponentModel.IContainer components = null;
protected override void Dispose(bool
disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
private
void InitializeComponent()
{
}
#endregion
}
}
A partir de la tout est possible grace au propriete du PropertyDescriptor et au GetProperties() de chaque objet.
Le type de selection s'arrete au meme endroit que l'imagination du dev.
Il est donc possible d'afficher seulement les proprietes/categories desirees, les proprietes d'un classe. Ou alors raisonner dans le sens contraire, ne pas afficher les proprites d'un classe, categorie, ou autre.
Je tiens tout de meme a souligner que cet exemple porte sur les proprietes mais bien d'autres choses sont interceptables via cette interface...
Voila j'espere que ca pourra servir a quelqu'un un jour et que c'est assez clair.
N'hesitez pas a faire des remarques, meme mauvaise, ca pourrait m'etre constructif par la suite.
Ce post vous a plu ? Ajoutez le dans vos favoris pour ne pas perdre de temps à le retrouver le jour où vous en aurez besoin :