Ich habe genau dies mit einer ICustomTypeDescriptor-Schnittstelle und einem Wörterbuch getan.
Implementieren von ICustomTypeDescriptor für dynamische Eigenschaften:
Vor kurzem musste eine Rasteransicht an ein Datensatzobjekt gebunden werden, das eine beliebige Anzahl von Eigenschaften aufweisen kann, die zur Laufzeit hinzugefügt und entfernt werden können. Auf diese Weise konnte ein Benutzer einer Ergebnismenge eine neue Spalte hinzufügen, um einen zusätzlichen Datensatz einzugeben.
Dies kann erreicht werden, indem jede Datenzeile als Wörterbuch verwendet wird, wobei der Schlüssel der Eigenschaftsname und der Wert eine Zeichenfolge oder eine Klasse ist, in der der Wert der Eigenschaft für die angegebene Zeile gespeichert werden kann. Natürlich kann eine Liste von Wörterbuchobjekten nicht an ein Raster gebunden werden. Hier kommt der ICustomTypeDescriptor ins Spiel.
Durch Erstellen einer Wrapper-Klasse für das Dictionary und deren Einhaltung der ICustomTypeDescriptor-Schnittstelle kann das Verhalten für die Rückgabe von Eigenschaften für ein Objekt überschrieben werden.
Schauen Sie sich die Implementierung der Datenklassenzeile unten an:
/// <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
//---------------------------------------------------------------------------------------------------------------------------
}
Hinweis: In der GetProperties-Methode konnte ich die einmal gelesenen PropertyDescriptors für die Leistung zwischenspeichern, aber da ich zur Laufzeit Spalten hinzufüge und entferne, möchte ich immer, dass sie neu erstellt werden
Sie werden auch in der GetProperties-Methode feststellen, dass die für die Wörterbucheinträge hinzugefügten Eigenschaftsbeschreibungen vom Typ TestResultPropertyDescriptor sind. Dies ist eine benutzerdefinierte Property Descriptor-Klasse, die verwaltet, wie Eigenschaften festgelegt und abgerufen werden. Schauen Sie sich die Implementierung unten an:
/// <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
//---------------------------------------------------------------------------------------------------------------------------
}
Die wichtigsten Eigenschaften dieser Klasse sind GetValue und SetValue. Hier sehen Sie, wie die Komponente als Wörterbuch umgewandelt wird und der Wert des darin enthaltenen Schlüssels festgelegt oder abgerufen wird. Es ist wichtig, dass das Wörterbuch in dieser Klasse vom selben Typ in der Row-Wrapper-Klasse ist, da sonst die Umwandlung fehlschlägt. Beim Erstellen des Deskriptors wird der Schlüssel (Eigenschaftsname) übergeben und zum Abfragen des Wörterbuchs verwendet, um den richtigen Wert zu erhalten.
Entnommen aus meinem Blog unter:
ICustomTypeDescriptor Implementierung für dynamische Eigenschaften