Ho fatto esattamente questo con un'interfaccia ICustomTypeDescriptor e un dizionario.
Implementazione di ICustomTypeDescriptor per proprietà dinamiche:
Recentemente ho avuto la necessità di associare una visualizzazione griglia a un oggetto record che potrebbe avere un numero qualsiasi di proprietà che possono essere aggiunte e rimosse in fase di esecuzione. Questo per consentire a un utente di aggiungere una nuova colonna a un set di risultati per inserire un ulteriore set di dati.
Ciò può essere ottenuto utilizzando ciascuna "riga" di dati come dizionario con la chiave come nome della proprietà e il valore come una stringa o una classe in grado di memorizzare il valore della proprietà per la riga specificata. Ovviamente, avere un elenco di oggetti Dizionario non sarà in grado di essere associato a una griglia. È qui che entra in gioco ICustomTypeDescriptor.
Creando una classe wrapper per il Dictionary e facendolo aderire all'interfaccia ICustomTypeDescriptor, è possibile sovrascrivere il comportamento per la restituzione delle proprietà per un oggetto.
Dai un'occhiata all'implementazione della classe data 'row' di seguito:
public class TestResultRowWrapper : Dictionary<string, TestResultValue>, ICustomTypeDescriptor
{
#region Methods
AttributeCollection ICustomTypeDescriptor.GetAttributes()
{
return new AttributeCollection(null);
}
string ICustomTypeDescriptor.GetClassName()
{
return null;
}
string ICustomTypeDescriptor.GetComponentName()
{
return null;
}
TypeConverter ICustomTypeDescriptor.GetConverter()
{
return null;
}
EventDescriptor ICustomTypeDescriptor.GetDefaultEvent()
{
return null;
}
PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty()
{
return null;
}
object ICustomTypeDescriptor.GetEditor(Type editorBaseType)
{
return null;
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents(Attribute[] attributes)
{
return new EventDescriptorCollection(null);
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents()
{
return new EventDescriptorCollection(null);
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties(Attribute[] attributes)
{
List<propertydescriptor> properties = new List<propertydescriptor>();
foreach (string key in this.Keys)
{
properties.Add(new TestResultPropertyDescriptor(key));
}
PropertyDescriptorCollection pdc = TypeDescriptor.GetProperties(this.GetType(), attributes);
foreach (PropertyDescriptor oPropertyDescriptor in pdc)
{
properties.Add(oPropertyDescriptor);
}
return new PropertyDescriptorCollection(properties.ToArray());
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
{
return ((ICustomTypeDescriptor)this).GetProperties(null);
}
object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor pd)
{
return this;
}
#endregion Methods
}
Nota: nel metodo GetProperties potrei memorizzare nella cache i PropertyDescriptors una volta letti per le prestazioni, ma poiché sto aggiungendo e rimuovendo colonne in fase di esecuzione, voglio sempre che vengano ricostruiti
Noterai anche nel metodo GetProperties che i descrittori di proprietà aggiunti per le voci del dizionario sono di tipo TestResultPropertyDescriptor. Si tratta di una classe descrittore di proprietà personalizzata che gestisce il modo in cui le proprietà vengono impostate e recuperate. Dai un'occhiata all'implementazione di seguito:
public class TestResultPropertyDescriptor : PropertyDescriptor
{
#region Properties
public override Type ComponentType
{
get { return typeof(Dictionary<string, TestResultValue>); }
}
public override bool IsReadOnly
{
get { return false; }
}
public override Type PropertyType
{
get { return typeof(string); }
}
#endregion Properties
#region Constructor
public TestResultPropertyDescriptor(string key)
: base(key, null)
{
}
#endregion Constructor
#region Methods
public override bool CanResetValue(object component)
{
return true;
}
public override object GetValue(object component)
{
return ((Dictionary<string, TestResultValue>)component)[base.Name].Value;
}
public override void ResetValue(object component)
{
((Dictionary<string, TestResultValue>)component)[base.Name].Value = string.Empty;
}
public override void SetValue(object component, object value)
{
((Dictionary<string, TestResultValue>)component)[base.Name].Value = value.ToString();
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
#endregion Methods
}
Le proprietà principali da esaminare in questa classe sono GetValue e SetValue. Qui puoi vedere il componente di cui viene eseguito il cast come dizionario e il valore della chiave al suo interno che viene impostato o recuperato. È importante che il dizionario in questa classe sia dello stesso tipo nella classe wrapper Row, altrimenti il cast fallirà. Quando viene creato il descrittore, la chiave (nome della proprietà) viene passata e viene utilizzata per interrogare il dizionario per ottenere il valore corretto.
Tratto dal mio blog all'indirizzo:
ICustomTypeDescriptor Implementazione per proprietà dinamiche