J'ai fait exactement cela avec une interface ICustomTypeDescriptor et un dictionnaire.
Implémentation d'ICustomTypeDescriptor pour les propriétés dynamiques:
J'ai récemment eu l'obligation de lier une vue de grille à un objet d'enregistrement qui pourrait avoir un nombre quelconque de propriétés pouvant être ajoutées et supprimées au moment de l'exécution. Il s'agissait de permettre à un utilisateur d'ajouter une nouvelle colonne à un jeu de résultats pour entrer un jeu de données supplémentaire.
Ceci peut être réalisé en ayant chaque «ligne» de données comme un dictionnaire avec la clé étant le nom de la propriété et la valeur étant une chaîne ou une classe qui peut stocker la valeur de la propriété pour la ligne spécifiée. Bien sûr, avoir une liste d'objets Dictionary ne pourra pas être lié à une grille. C'est là qu'intervient ICustomTypeDescriptor.
En créant une classe wrapper pour le dictionnaire et en le faisant adhérer à l'interface ICustomTypeDescriptor, le comportement de renvoi des propriétés d'un objet peut être remplacé.
Jetez un œil à l'implémentation de la classe de données 'row' ci-dessous:
/// <summary>
/// Class to manage test result row data functions
/// </summary>
public class TestResultRowWrapper : Dictionary<string, TestResultValue>, ICustomTypeDescriptor
{
//- METHODS -----------------------------------------------------------------------------------------------------------------
#region Methods
/// <summary>
/// Gets the Attributes for the object
/// </summary>
AttributeCollection ICustomTypeDescriptor.GetAttributes()
{
return new AttributeCollection(null);
}
/// <summary>
/// Gets the Class name
/// </summary>
string ICustomTypeDescriptor.GetClassName()
{
return null;
}
/// <summary>
/// Gets the component Name
/// </summary>
string ICustomTypeDescriptor.GetComponentName()
{
return null;
}
/// <summary>
/// Gets the Type Converter
/// </summary>
TypeConverter ICustomTypeDescriptor.GetConverter()
{
return null;
}
/// <summary>
/// Gets the Default Event
/// </summary>
/// <returns></returns>
EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
{
return null;
}
/// <summary>
/// Gets the Default Property
/// </summary>
PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
{
return null;
}
/// <summary>
/// Gets the Editor
/// </summary>
object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
{
return null;
}
/// <summary>
/// Gets the Events
/// </summary>
EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
{
return new EventDescriptorCollection(null);
}
/// <summary>
/// Gets the events
/// </summary>
EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
{
return new EventDescriptorCollection(null);
}
/// <summary>
/// Gets the properties
/// </summary>
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
{
List<propertydescriptor> properties = new List<propertydescriptor>();
//Add property descriptors for each entry in the dictionary
foreach (string key in this.Keys)
{
properties.Add(new TestResultPropertyDescriptor(key));
}
//Get properties also belonging to this class also
PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties(this.GetType(), attributes);
foreach (PropertyDescriptor oPropertyDescriptor in pdc)
{
properties.Add(oPropertyDescriptor);
}
return new PropertyDescriptorCollection(properties.ToArray());
}
/// <summary>
/// gets the Properties
/// </summary>
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
{
return ((ICustomTypeDescriptor)this).GetProperties(null);
}
/// <summary>
/// Gets the property owner
/// </summary>
object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
{
return this;
}
#endregion Methods
//---------------------------------------------------------------------------------------------------------------------------
}
Remarque: dans la méthode GetProperties, je pourrais mettre en cache les PropertyDescriptors une fois lus pour les performances, mais comme j'ajoute et supprime des colonnes au moment de l'exécution, je veux toujours les reconstruire
Vous remarquerez également dans la méthode GetProperties que les descripteurs de propriété ajoutés pour les entrées de dictionnaire sont de type TestResultPropertyDescriptor. Il s'agit d'une classe de descripteur de propriété personnalisée qui gère la manière dont les propriétés sont définies et récupérées. Jetez un œil à l'implémentation ci-dessous:
/// <summary>
/// Property Descriptor for Test Result Row Wrapper
/// </summary>
public class TestResultPropertyDescriptor : PropertyDescriptor
{
//- PROPERTIES --------------------------------------------------------------------------------------------------------------
#region Properties
/// <summary>
/// Component Type
/// </summary>
public override Type ComponentType
{
get { return typeof(Dictionary<string, TestResultValue>); }
}
/// <summary>
/// Gets whether its read only
/// </summary>
public override bool IsReadOnly
{
get { return false; }
}
/// <summary>
/// Gets the Property Type
/// </summary>
public override Type PropertyType
{
get { return typeof(string); }
}
#endregion Properties
//- CONSTRUCTOR -------------------------------------------------------------------------------------------------------------
#region Constructor
/// <summary>
/// Constructor
/// </summary>
public TestResultPropertyDescriptor(string key)
: base(key, null)
{
}
#endregion Constructor
//- METHODS -----------------------------------------------------------------------------------------------------------------
#region Methods
/// <summary>
/// Can Reset Value
/// </summary>
public override bool CanResetValue(object component)
{
return true;
}
/// <summary>
/// Gets the Value
/// </summary>
public override object GetValue(object component)
{
return ((Dictionary<string, TestResultValue>)component)[base.Name].Value;
}
/// <summary>
/// Resets the Value
/// </summary>
public override void ResetValue(object component)
{
((Dictionary<string, TestResultValue>)component)[base.Name].Value = string.Empty;
}
/// <summary>
/// Sets the value
/// </summary>
public override void SetValue(object component, object value)
{
((Dictionary<string, TestResultValue>)component)[base.Name].Value = value.ToString();
}
/// <summary>
/// Gets whether the value should be serialized
/// </summary>
public override bool ShouldSerializeValue(object component)
{
return false;
}
#endregion Methods
//---------------------------------------------------------------------------------------------------------------------------
}
Les principales propriétés à examiner sur cette classe sont GetValue et SetValue. Ici, vous pouvez voir le composant en cours de conversion en tant que dictionnaire et la valeur de la clé à l'intérieur en cours de définition ou de récupération. Il est important que le dictionnaire de cette classe soit du même type dans la classe wrapper Row, sinon la conversion échouera. Lorsque le descripteur est créé, la clé (nom de propriété) est transmise et est utilisée pour interroger le dictionnaire pour obtenir la valeur correcte.
Tiré de mon blog à:
Implémentation ICustomTypeDescriptor pour les propriétés dynamiques