DataGridView 数据绑定到 List<List<T>> [英] DataGridView Databinding to List<List<T>>
问题描述
给定代码
class Foo {公共字符串值 {get;放;}公共 int Id {get;放;}}列表<列表<Foo>>fooList = new List>();
有没有办法将 Multidim ICollection 绑定到属性 Value 上的 DataGridView,当您更改单元格时,对象的 Value 属性会更新?
在这种情况下,列表中的每个 Foo 实例将代表 DataGridView 中的一个单元格,并且行/列将被保留,就像它们在 multidim ICollection 中一样
Multidim 的意思是:
List] =>[列表=>[0,1,2,3,4,5]列表=>[0,1,2,3,4,5]列表=>[0,1,2,3,4,5]列表=>[0,1,2,3,4,5]]
嵌套列表中的每个元素实际上是 Foo 的实例.
内部实现 IListSource 并映射到 DataTabe
您可以创建一个自定义数据源来实现
而当您在 DataGridView 中编辑数据时,实际上您正在编辑原始列表.
另外,如果你想隐藏一个属性,就像在属性中添加[Browsable(false)]
一样简单:
公共类Foo{[可浏览(假)]公共 int Id { 获取;放;}公共字符串值 { 获取;放;}}
Given the code
class Foo {
public string Value {get; set;}
public int Id {get; set;}
}
List<List<Foo>> fooList = new List<List<Foo>>();
Is there a way to bind a Multidim ICollection to a DataGridView on the property Value, where when you change a cell, the Value property of the object updates?
In this case, each instance of Foo in the list will represent one cell in the DataGridView and the rows/ columns are being preserved as they would be in the multidim ICollection
By Multidim I mean something to the affect of:
List<List<Foo>] => [
List<Foo> => [0,1,2,3,4,5]
List<Foo> => [0,1,2,3,4,5]
List<Foo> => [0,1,2,3,4,5]
List<Foo> => [0,1,2,3,4,5]
]
Where each element in the nested list is actually and instance of Foo.
Implementing IListSource and mapping to DataTabe internally
You can create a custom data source which implements IListSource and set it as data source of DataGridView. To implement the interface properly to satisfy your requirement:
- In constructor, accept original list and map it to a
DataTable
. - Subscribe to
ListChanged
event of theDefaultView
property of you data table and apply changes to your original list. - For
GetList
method, return the mapped data table.
Then when you bind DataGridView to your new data source, all the editing operations will immediately reflect in your original list:
dataGridView1.DataSource = new FooDataSource(yourListOfListOfFoo);
ListListDataSource Implementation
public class ListListDataSource<T> : IListSource
{
List<List<T>> data;
DataTable table;
public ListListDataSource(List<List<T>> list)
{
this.data = list;
table = new DataTable();
for (int i = 0; i < list.First().Count(); i++)
{
TypeDescriptor.GetProperties(typeof(T)).Cast<PropertyDescriptor>()
.Where(p => p.IsBrowsable).ToList().ForEach(p =>
{
if (p.IsBrowsable)
{
var c = new DataColumn($"[{i}].{p.Name}", p.PropertyType);
c.ReadOnly = p.IsReadOnly;
table.Columns.Add(c);
}
});
}
foreach (var innerList in list)
{
table.Rows.Add(innerList.SelectMany(
x => TypeDescriptor.GetProperties(typeof(T)).Cast<PropertyDescriptor>()
.Where(p => p.IsBrowsable).Select(p => p.GetValue(x))).ToArray());
}
table.DefaultView.AllowDelete = false;
table.DefaultView.AllowNew = false;
table.DefaultView.ListChanged += DefaultView_ListChanged;
}
public bool ContainsListCollection => false;
public IList GetList()
{
return table.DefaultView;
}
private void DefaultView_ListChanged(object sender, ListChangedEventArgs e)
{
if (e.ListChangedType != ListChangedType.ItemChanged)
throw new NotSupportedException();
var match = Regex.Match(e.PropertyDescriptor.Name, @"\[(\d+)\]\.(\w+)");
var index = int.Parse(match.Groups[1].Value);
var propertyName = match.Groups[2].Value;
typeof(T).GetProperty(propertyName).SetValue(data[e.NewIndex][index],
table.Rows[e.NewIndex][e.PropertyDescriptor.Name]);
}
}
Then bind your list to DataGridView
like this:
List<List<Foo>> foos;
private void Form1_Load(object sender, EventArgs e)
{
foos = new List<List<Foo>>{
new List<Foo>(){
new Foo() { Id=11, Value="11"}, new Foo() { Id = 12, Value = "12" }
},
new List<Foo>() {
new Foo() { Id=21, Value="21"}, new Foo() { Id = 22, Value = "22" }
},
};
dataGridView1.DataSource = new ListListDataSource<Foo>(foos);
}
And when you edit data in DataGridView, in fact you are editing the original list.
Also if you want to hide a property, it's as easy as adding [Browsable(false)]
to the property:
public class Foo
{
[Browsable(false)]
public int Id { get; set; }
public string Value { get; set; }
}
这篇关于DataGridView 数据绑定到 List<List<T>>的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!