Добро пожаловать в удивительный мир System.ComponentModel. Этот темный угол .NET очень мощный, но очень сложный.
Слово предостережения; если у вас не достаточно времени для этого - вы можете просто сериализовать его в любом механизме, которым вы довольны, но перевести его обратно в DataTable
на каждом конце ... что не для слабонервных ; р
Во-первых, привязка данных (для таблиц) работает против списков (IList
/ IListSource
) - поэтому List<T>
должно быть в порядке (отредактировано: я что-то неправильно прочитал). Но он не поймет, что ваш словарь на самом деле является столбцами ...
Чтобы получить тип, претендующий на наличие столбцов, вам нужно использовать пользовательские реализации PropertyDescriptor
. Есть несколько способов сделать это, в зависимости от того, всегда ли определения столбцов одинаковы (но определяются во время выполнения, т. Е. Возможно, из конфигурации), или же они меняются в зависимости от использования (например, как каждый экземпляр DataTable
может иметь разные столбцы).
Для настройки "на экземпляр" вам нужно взглянуть на ITypedList
- этот зверь (реализован в добавление к IList
) имеет интересную задачу представления свойств для табличных данных ... но это не один:
Для настройки "по типу" вы можете посмотреть TypeDescriptionProvider
- это может предложить динамические свойства для класса ...
... или вы можете реализовать ICustomTypeDescriptor
- но это используется только (для списков) в очень случайных обстоятельствах (индексатор объектов (public object this[int index] {get;}
") и по крайней мере одна строка в список в точке привязки). (этот интерфейс гораздо более полезен при привязке дискретных объектов - т.е. не списков).
Внедрение ITypedList
и предоставление модели PropertyDescriptor
- тяжелая работа ... следовательно, это делается очень редко. Я довольно знаком с этим, но я бы не стал делать это только ради смеха ...
Вот очень, очень упрощенная реализация (все столбцы являются строками; без уведомлений (через дескриптор), без проверки (IDataErrorInfo
), без преобразований (TypeConverter
), без поддержки дополнительного списка ( IBindingList
/ IBindingListView
), без абстракции (IListSource
), без других метаданных / атрибутов и т. Д.):
using System.ComponentModel;
using System.Collections.Generic;
using System;
using System.Windows.Forms;
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
PropertyBagList list = new PropertyBagList();
list.Columns.Add("Foo");
list.Columns.Add("Bar");
list.Add("abc", "def");
list.Add("ghi", "jkl");
list.Add("mno", "pqr");
Application.Run(new Form {
Controls = {
new DataGridView {
Dock = DockStyle.Fill,
DataSource = list
}
}
});
}
}
class PropertyBagList : List<PropertyBag>, ITypedList
{
public PropertyBag Add(params string[] args)
{
if (args == null) throw new ArgumentNullException("args");
if (args.Length != Columns.Count) throw new ArgumentException("args");
PropertyBag bag = new PropertyBag();
for (int i = 0; i < args.Length; i++)
{
bag[Columns[i]] = args[i];
}
Add(bag);
return bag;
}
public PropertyBagList() { Columns = new List<string>(); }
public List<string> Columns { get; private set; }
PropertyDescriptorCollection ITypedList.GetItemProperties(PropertyDescriptor[] listAccessors)
{
if(listAccessors == null || listAccessors.Length == 0)
{
PropertyDescriptor[] props = new PropertyDescriptor[Columns.Count];
for(int i = 0 ; i < props.Length ; i++)
{
props[i] = new PropertyBagPropertyDescriptor(Columns[i]);
}
return new PropertyDescriptorCollection(props, true);
}
throw new NotImplementedException("Relations not implemented");
}
string ITypedList.GetListName(PropertyDescriptor[] listAccessors)
{
return "Foo";
}
}
class PropertyBagPropertyDescriptor : PropertyDescriptor
{
public PropertyBagPropertyDescriptor(string name) : base(name, null) { }
public override object GetValue(object component)
{
return ((PropertyBag)component)[Name];
}
public override void SetValue(object component, object value)
{
((PropertyBag)component)[Name] = (string)value;
}
public override void ResetValue(object component)
{
((PropertyBag)component)[Name] = null;
}
public override bool CanResetValue(object component)
{
return true;
}
public override bool ShouldSerializeValue(object component)
{
return ((PropertyBag)component)[Name] != null;
}
public override Type PropertyType
{
get { return typeof(string); }
}
public override bool IsReadOnly
{
get { return false; }
}
public override Type ComponentType
{
get { return typeof(PropertyBag); }
}
}
class PropertyBag
{
private readonly Dictionary<string, string> values
= new Dictionary<string, string>();
public string this[string key]
{
get
{
string value;
values.TryGetValue(key, out value);
return value;
}
set
{
if (value == null) values.Remove(key);
else values[key] = value;
}
}
}