Привязка данных динамические данные - PullRequest
27 голосов
/ 19 мая 2009

У меня есть набор «динамических данных», которые мне нужно привязать к GridControl. До сих пор я использовал стандартный класс DataTable, который является частью пространства имен System.Data. Это работало нормально, но мне сказали, что я не могу использовать это, так как он слишком тяжел для сериализации по сети между клиентом и сервером.

Так что я подумал, что мог бы легко скопировать «урезанную» версию класса DataTable, просто имея тип List<Dictionary<string, object>>, причем список представляет коллекцию строк, а каждый словарь представляет одну строку с именами столбцов и значения в качестве типа KeyValuePair. Я мог бы настроить Grid так, чтобы свойства столбца DataField соответствовали свойствам ключей в Словаре (так же, как я делал для имен столбцов DataTable.

Однако после выполнения

gridControl.DataSource = table;
gridControl.RefreshDataSource();

Сетка не имеет данных ...

Я думаю, мне нужно реализовать IEnumerator - любая помощь по этому вопросу будет высоко ценится!

Пример кода вызова выглядит следующим образом:

var table = new List<Dictionary<string,object>>();

var row = new Dictionary<string, object>
{
    {"Field1", "Data1"},
    {"Field2", "Data2"},
    {"Field3", "Data3"}
};

table.Add(row);

gridControl1.DataSource = table;
gridControl1.RefreshDataSource();

1 Ответ

63 голосов
/ 19 мая 2009

Добро пожаловать в удивительный мир 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;
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...