Я зробив саме це за допомогою інтерфейсу ICustomTypeDescriptor та словника.
Реалізація ICustomTypeDescriptor для динамічних властивостей:
Нещодавно у мене була вимога прив’язати подання сітки до об’єкту запису, який може мати будь-яку кількість властивостей, які можна додати та видалити під час виконання. Це мало дозволити користувачеві додати новий стовпець до набору результатів, щоб ввести додатковий набір даних.
Цього можна досягти, маючи кожен рядок даних як словник, причому ключем є ім'я властивості, а значенням є рядок або клас, який може зберігати значення властивості для зазначеного рядка. Звичайно, наявність списку об’єктів словника неможливо буде прив’язати до сітки. Тут з’являється ICustomTypeDescriptor.
Створюючи обгортковий клас для Словника та змушуючи його дотримуватися інтерфейсу ICustomTypeDescriptor, поведінку для повернення властивостей об’єкта можна замінити.
Погляньте на реалізацію класу "рядок" даних нижче:
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
}
Примітка: У методі GetProperties я міг кешувати PropertyDescriptors один раз прочитаним для продуктивності, але оскільки я додаю та видаляю стовпці під час виконання, я завжди хочу, щоб їх відбудовували
Ви також помітите в методі GetProperties, що дескриптори властивостей, додані до словникових записів, мають тип TestResultPropertyDescriptor. Це власний клас Descriptor властивостей, який керує способом встановлення та отримання властивостей. Погляньте на реалізацію нижче:
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
}
Основними властивостями, на які слід звернути увагу в цьому класі, є GetValue та SetValue. Тут ви можете побачити компонент, який відливається як словник, і значення ключа всередині нього, який встановлюється або отримується. Важливо, щоб словник у цьому класі був однотипним у класі обгортки рядків, інакше приведення не вдасться. Коли дескриптор створений, ключ (ім'я властивості) передається і використовується для запиту словника, щоб отримати правильне значення.
Взято з мого блогу за адресою:
Впровадження ICustomTypeDescriptor для динамічних властивостей