数据绑定动态数据

Posted

技术标签:

【中文标题】数据绑定动态数据【英文标题】:Data binding dynamic data 【发布时间】:2010-10-27 07:14:59 【问题描述】:

我有一组需要绑定到 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();

【问题讨论】:

网格控制?你是说DataGridView吗? 【参考方案1】:

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

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

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

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

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

对于“每种类型”的自定义,您可以查看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;
        
    

【讨论】:

哎哟......我的眼睛在流血。我回到数据集和动态列集合:D @MarcGravell 您将如何解决在上述示例中实现许多不同 sql 类型的问题?不使用对象..? @John 你能说得更具体点吗?特别是,我们什么时候开始谈论 SQL 类型的?乐于提供帮助,但需要上下文。 @MarcGravell 抱歉,来晚了,我累了。我的意思是,如何重写 PropertyBag 来存储和返回不同的类型?没有对象类型可以做到吗? @John 对;你可以用PropertyBag 做更复杂的事情——你只需要PropertyDescriptorPropertyType 来返回正确的东西。然而,正如我所提到的——这实际上是重复了很多工作,使其逐渐接近DataTable 所做的工作。我不喜欢DataTable,但在处理灵活模式时它是一个合理的选择。

以上是关于数据绑定动态数据的主要内容,如果未能解决你的问题,请参考以下文章

WPF 动态列(DataGridTemplateColumn) 绑定数据 (自定义控件)对象绑定

aspxgridview控件模板从表如何动态绑定数据绑定数据

C#怎么实现下拉框动态绑定数据

数据绑定动态数据

动态绑定剑道网格数据源绑定到指令

Vue.js 动态绑定class