C# Dictionary绑定datagridview

2025-02-24 12:39:44
推荐回答(2个)
回答1:

你诶,离胜利只差一步了哟。

跨线程错误我就不说了。

你想要的是深入绑定,即能够绑定到元素项的内部属性,甚至内部属性的内部属性。


先说一下绑定原理,不论是dgv还是BindingSource的DataSource绑定,都需要数据获取者指明DataPropertyName,这要求绑定数据源:首先继承了IEnumrable等一系列接口,其次T包含有至少可get的属性才可使用绑定。对于你的情况,如果使用BindingSource直绑Dictionary,你的泛型T就是KeyValuePair,其拥有公开的属性Key、Value、Count等等,此时数据获取者要填写的DataPropertyName可以是“Key”,“Value”,“Count”等等。


当然我们的需求是花样百出的,还是以你的情况为例,难免会出现Value和Key是自己定义的类型,此时如果不改DataPropertyName,你得到的只会是该类型的名称字符串。想要绑定到自定义类型的内部甚至更内部,有至少二种办法:


一、继承系统的BindingSource,overide及新增一些方法,可以让你在使用MyBindingSource mbs=new MyBindingSource(...)时直接指明你需要绑定的内部元素。这个过程开销很小但是编写有一定难度,需要在BindingSource内部重新维护一个合适你需要的绑定元素列表,并且需要处理绑定属性名的问题。此技术在大型项目中比较多用,好处是一次改完哪里都能用,而且可以支持双向绑定,允许反向编辑真实的数据源。有兴趣的话可以PM我我写个简单的给你。


二、修改提供给BindingSource或者dgv.DataSouce的数据源,也就是直接抽取内部属性出来构建一个新的绑定数据源。比较适合针对某个特定表的绑定,也是最容易实现的。当然,因为你是新构建了用于显示的数据源,只能支持单向绑定咯。

假定你的InnerClass是这样写的:

public class MyData
    {
        public string C1 { get; set; }
        public string C2 { get; set; }
        public string C3 { get; set; }
    }

如果需要用两列分别绑定Dictionary dic的Key和MyData.C1,一行搞定:

dataGridView1.DataSource=(from i in dic select new{i.Key,i.MyData.C1}).ToList();
本质上就是遍历字典,把每一个键值对的Key和Value.C1都抽出来做成新字典然后转列表。
如果不熟悉Linq,也可以自己写一个抽取数据的小方法每次绑定前转换下。


嗯,刚才我同事也给了个很中二的办法:重写dataGridView。通过重写绑定相关的方法,可以让DataGridView能够识别形如"Value.C1"甚至“Value.C1.Length"这样的DataProperyName并且在绑定事件中能够通过反射查找Value内部是否存在C1属性,如果该属性存在则将其呈现。

这个可以一试,目测难度较大。


和同事小聊了下我又想起一个另类的办法- -|

我们知道不管你绑的是什么值,想在界面上看到都有一个转换过程。这里DataGridView描绘每个含绑定的单元格时都会把对应的数据源中的绑定元素的ToString()值拿来呈现。如果你只有一列需要绑定到InnerClass的C1属性,我们可以这样取巧:

首先dgv里的两列的DataPropertyName仍然写的是”Key“和"Value”,这样第二列绑到的是一个InnerClass对象,只不过其显示出来的ToString()是形如“MyNamespace.InnerClass”这样的类名字符串,如果我们重写InnerClass的ToString()方法呢?测试完发现一切OK。也只需要在InnerClass里加一句:

public override string ToString()
        {
            return C1;
        }


以上四种方案你挑着用吧。我相信如果你愿意投一点时间多从其他角度看问题的话,你会找到更多解决方案的。为了生活有时我们不得不只抓效率抓进度,但闲暇时做点小研究其实也是挺惬意的一个事情,并非是得不偿失~


还有什么问题可PM我。

回答2:

报跨线程错误就使用invok或者是最简单的在this.dataGridView.DataSource = tmpMap.ToList>()上面写上Control.CheckForIllegalCrossThreadCalls = false;