C# 数据绑定动态数据
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/882214/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Data binding dynamic data
提问by Chris Moutray
I have a set of 'dynamic data' that I need to bind to the GridControl. Up until now, I have been using the standard DataTable class that's part of the System.Data namespace. This has worked fine, but I've been told I cannot use this as it's too heavy for serialization across the network between client & server.
我有一组需要绑定到 GridControl 的“动态数据”。到目前为止,我一直在使用作为 System.Data 命名空间一部分的标准 DataTable 类。这工作得很好,但我被告知我不能使用它,因为它对于客户端和服务器之间的网络序列化来说太重了。
So I thought I could easy replicate a 'cut-down' version of the DataTable class by simply having a type of List<Dictionary<string, object>>
whereby the List represents the collection of rows, and each Dictionary represents one row with the column names and values as a KeyValuePair type. I could set up the Grid to have the column DataField properties to match those of the keys in the Dictionary (just like I was doing for the DataTable's column names.
所以我想我可以通过简单的类型来轻松复制 DataTable 类的“缩减”版本,List<Dictionary<string, object>>
其中 List 代表行的集合,每个 Dictionary 代表一行,其中列名和值作为 KeyValuePair 类型。我可以设置 Grid 使列 DataField 属性与字典中的键匹配(就像我对 DataTable 的列名所做的那样。
However after doing
但是做完之后
gridControl.DataSource = table;
gridControl.RefreshDataSource();
The grid has no data...
网格没有数据...
I think I need to implement IEnumerator
- any help on this would be much appreciated!
我想我需要实施IEnumerator
- 对此的任何帮助将不胜感激!
Example calling code looks like this:
示例调用代码如下所示:
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();
采纳答案by Marc Gravell
Welcome to the wonderful world of System.ComponentModel. This dark corner of .NET is very powerful, but very complex.
欢迎来到 System.ComponentModel 的精彩世界。.NET 的这个黑暗角落非常强大,但非常复杂。
A word of caution; unless you have a lot of time for this - you may do well to simply serialize it in whatever mechanism you are happy with, but rehydrate it back into a DataTable
at each end... what follows is not for the faint-hearted ;-p
一个警告;除非你有很多时间来做这件事——你可以简单地以你喜欢的任何机制序列化它,但DataTable
在每一端将它重新水化成......接下来的内容不适合胆小的人;-p
Firstly - data binding (for tables) works against lists(IList
/IListSource
) - so List<T>
should be fine (edited: I misread something). But it isn't going to understand that your dictionary is actually columns...
首先 - 数据绑定(用于表)适用于列表(IList
/ IListSource
) - 所以List<T>
应该没问题(编辑:我误读了一些东西)。但它不会明白你的字典实际上是列......
To get a type to pretend to have columns you need to use custom PropertyDescriptor
implementations. There are several ways to do this, depending on whether the column definitions are always the same (but determined at runtime, i.e. perhaps from config), or whether it changes per usage (like how each DataTable
instance can have different columns).
要获得一种假装具有列的类型,您需要使用自定义PropertyDescriptor
实现。有几种方法可以做到这一点,这取决于列定义是否始终相同(但在运行时确定,即可能来自配置),或者它是否随使用而变化(例如每个DataTable
实例如何具有不同的列)。
For "per instance" customisation, you need to look at ITypedList
- this beast (implemented in additionto IList
) has the fun task of presenting properties for tabular data... but it isn't alone:
对于“每个实例”的定制,你需要看一下ITypedList
-这兽(中实现除了要IList
)有呈现表格数据属性的好玩的事......但它并不孤单:
For "per type" customisation, you can look at TypeDescriptionProvider
- this can suggest dynamic properties for a class...
对于“按类型”自定义,您可以查看TypeDescriptionProvider
- 这可以建议类的动态属性...
...or you can implement ICustomTypeDescriptor
- but this is only used (for lists) in veryoccasional circumstances (an object indexer (public object this[int index] {get;}
") and at least one row in the list at the point of binding). (this interface is much more useful when binding discrete objects - i.e. not lists).
...或者您可以实现ICustomTypeDescriptor
- 但这仅在非常偶然的情况下(用于列表)(对象索引器(public object this[int index] {get;}
“)和绑定点列表中的至少一行)。(此接口更有用绑定离散对象时 - 即不是列表)。
Implementing ITypedList
, and providing a PropertyDescriptor
model is hard work... hence it is only done very occasionally. I'm fairly familiar with it, but I wouldn't do it just for laughs...
实施ITypedList
和提供PropertyDescriptor
模型是一项艰巨的工作……因此它只是偶尔完成。我对它相当熟悉,但我不会仅仅为了笑而这样做......
Here's a very, very simplifiedimplementation (all columns are strings; no notifications (via descriptor), no validation (IDataErrorInfo
), no conversions (TypeConverter
), no additional list support (IBindingList
/IBindingListView
), no abstraction (IListSource
), no other other metadata/attributes, etc):
这是一个非常非常简单的实现(所有列都是字符串;没有通知(通过描述符),没有验证(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;
}
}
}