Zrobiłem dokładnie to za pomocą interfejsu ICustomTypeDescriptor i słownika.
Implementowanie ICustomTypeDescriptor dla właściwości dynamicznych:
Niedawno miałem wymóg powiązania widoku siatki z obiektem rekordu, który może mieć dowolną liczbę właściwości, które można dodawać i usuwać w czasie wykonywania. Miało to umożliwić użytkownikowi dodanie nowej kolumny do zestawu wyników w celu wprowadzenia dodatkowego zestawu danych.
Można to osiągnąć, mając każdy „wiersz” danych jako słownik, w którym klucz jest nazwą właściwości, a wartością jest ciąg lub klasa, która może przechowywać wartość właściwości dla określonego wiersza. Oczywiście posiadanie listy obiektów słownika nie będzie mogło być przypisane do siatki. W tym miejscu pojawia się ICustomTypeDescriptor.
Tworząc klasę opakowania dla Dictionary i dostosowując ją do interfejsu ICustomTypeDescriptor, można przesłonić zachowanie zwracania właściwości obiektu.
Spójrz na implementację klasy danych „wiersz” poniżej:
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
}
Uwaga: w metodzie GetProperties mogę buforować pliki PropertyDescriptors po przeczytaniu w celu zwiększenia wydajności, ale ponieważ dodaje i usuwa kolumny w czasie wykonywania, zawsze chcę, aby były odbudowywane
W metodzie GetProperties zauważysz również, że Deskryptory właściwości dodane do wpisów słownika są typu TestResultPropertyDescriptor. Jest to niestandardowa klasa deskryptora właściwości, która zarządza ustawieniem i pobieraniem właściwości. Spójrz na implementację poniżej:
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
}
Główne właściwości tej klasy to GetValue i SetValue. Tutaj możesz zobaczyć rzutowany komponent jako słownik oraz ustawioną lub pobraną wartość klucza w nim zawartego. Ważne jest, aby słownik w tej klasie był tego samego typu w klasie otoki Row, w przeciwnym razie rzutowanie zakończy się niepowodzeniem. Podczas tworzenia deskryptora klucz (nazwa właściwości) jest przekazywany i używany do wysyłania zapytań do słownika w celu uzyskania poprawnej wartości.
Zrobione z mojego bloga pod adresem:
Implementacja ICustomTypeDescriptor dla właściwości dynamicznych