c# - 数据绑定(bind)动态数据

标签 c# winforms data-binding

我有一组“动态数据”需要绑定(bind)到 GridControl。到目前为止,我一直在使用属于 System.Data 命名空间的标准 DataTable 类。这工作得很好,但我被告知我不能使用它,因为它对于客户端和服务器之间的网络序列化来说太重了。

所以我想我可以通过简单地拥有 List<Dictionary<string, object>> 类型来轻松复制 DataTable 类的“简化”版本其中 List 表示行的集合,每个 Dictionary 表示一行,其中列名和值作为 KeyValuePair 类型。我可以将 Grid 设置为具有列 DataField 属性以匹配 Dictionary 中键的属性(就像我对 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();

最佳答案

欢迎来到 System.ComponentModel 的精彩世界。 .NET 的这个黑暗角落非常强大,但也非常复杂。

请注意;除非你有很多时间 - 你可能会很好地简单地将它序列化为你喜欢的任何机制,但将它重新水化为 DataTable在每一端......接下来的内容不适合胆小的人;-p

首先 - 数据绑定(bind)(针对表)适用于列表 ( IList/IListSource ) - 所以 List<T>应该没问题(编辑:我误读了一些东西)。但它不会理解你的字典实际上是列......

要获得一个假装有列的类型,您需要使用自定义 PropertyDescriptor实现。有几种方法可以做到这一点,具体取决于列定义是否始终相同(但在运行时确定,即可能来自配置),或者它是否随使用而变化(例如每个 DataTable 实例如何可以有不同的列)。

对于“按实例”定制,您需要查看 ITypedList - 这个野兽(在 IListaddition 中实现)有一个有趣的任务来呈现表格数据的属性......但它并不孤单:

对于“按类型”定制,您可以查看TypeDescriptionProvider - 这可以建议类的动态属性...

...或者您可以实现 ICustomTypeDescriptor - 但这仅用于(列表)非常偶尔的情况(一个对象索引器(public object this[int index] {get;}“)和至少在绑定(bind)点列表中的一行)。(这个接口(interface)是在绑定(bind)离散对象时更有用 - 即不是列表)。

实现 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;
        }
    }
}

关于c# - 数据绑定(bind)动态数据,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/882214/

相关文章:

c# - 将此 C# 代码转换为经典 ASP/VBScript?

c# - 解决 WinForms 中的跨线程异常

java - 用于字符串列表的 Initbinder

c# - 动态绑定(bind)到 DataTemplate

c# - 在 SortedDictionary 中找到第一个未使用的键的快速方法?

c# - 如何通过动态编译使用自定义类

c# - 来自 launchsettings.json 的环境变量正在测试中使用吗?

c# - Winforms 方法/事件过滤器属性

c# - 使用撤消和重做功能绘制形状和字符串

wpf - 如何在 WPF 的 DataTemplate 中访问根 DataContext?