C# WinForms DataGridView-数据绑定到具有列表属性(可变列数)的对象
我有一个.NET类,我想在DataGridView中显示,默认的数据绑定——将DGV的数据源设置为对象——产生了我90%的需求(即,它正确输出公共属性,我可以轻松添加排序) 但是,我需要绑定的属性之一是一个列表,其中包含的数据需要位于其他数据绑定项之后的单独列中。我被困在如何最好地实现这一点上 我的班级看起来像这样:C# WinForms DataGridView-数据绑定到具有列表属性(可变列数)的对象,c#,.net,winforms,data-binding,datagridview,C#,.net,Winforms,Data Binding,Datagridview,我有一个.NET类,我想在DataGridView中显示,默认的数据绑定——将DGV的数据源设置为对象——产生了我90%的需求(即,它正确输出公共属性,我可以轻松添加排序) 但是,我需要绑定的属性之一是一个列表,其中包含的数据需要位于其他数据绑定项之后的单独列中。我被困在如何最好地实现这一点上 我的班级看起来像这样: public class BookDetails { public string Title { get; set; } public int TotalRatin
public class BookDetails
{
public string Title { get; set; }
public int TotalRating { get; set; }
public int Occurrence { get; set; }
public List<int> Rating { get; set; }
}
公共类书籍详细信息
{
公共字符串标题{get;set;}
公共整数总体评级{get;set;}
公共整数出现{get;set;}
公共列表评级{get;set;}
}
理想情况下,我可以将该Rating属性扩展为多个数字列,以便在运行时提供如下输出:
标题|总评级|发生率| R1 | R2 | R3。。。注册护士
将总评分计算为所有单个评分的总和也很有用,但我现在正在手动更新,没有问题。像这样吗
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Windows.Forms;
public class BookDetails
{
public string Title { get; set; }
public int TotalRating { get; set; }
public int Occurrence { get; set; }
public List<int> Rating { get; set; }
}
class BookList : List<BookDetails>, ITypedList
{
public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
{
var origProps = TypeDescriptor.GetProperties(typeof(BookDetails));
List<PropertyDescriptor> newProps = new List<PropertyDescriptor>(origProps.Count);
PropertyDescriptor doThisLast = null;
foreach (PropertyDescriptor prop in origProps)
{
if (prop.Name == "Rating") doThisLast = prop;
else newProps.Add(prop);
}
if (doThisLast != null)
{
var max = (from book in this
let rating = book.Rating
where rating != null
select (int?)rating.Count).Max() ?? 0;
if (max > 0)
{
// want it nullable to account for jagged arrays
Type propType = typeof(int?); // could also figure this out from List<T> in
// the general case, but make it nullable
for (int i = 0; i < max; i++)
{
newProps.Add(new ListItemDescriptor(doThisLast, i, propType));
}
}
}
return new PropertyDescriptorCollection(newProps.ToArray());
}
public string GetListName(PropertyDescriptor[] listAccessors)
{
return "";
}
}
class ListItemDescriptor : PropertyDescriptor
{
private static readonly Attribute[] nix = new Attribute[0];
private readonly PropertyDescriptor tail;
private readonly Type type;
private readonly int index;
public ListItemDescriptor(PropertyDescriptor tail, int index, Type type) : base(tail.Name + "[" + index + "]", nix)
{
this.tail = tail;
this.type = type;
this.index = index;
}
public override object GetValue(object component)
{
IList list = tail.GetValue(component) as IList;
return (list == null || list.Count <= index) ? null : list[index];
}
public override Type PropertyType
{
get { return type; }
}
public override bool IsReadOnly
{
get { return true; }
}
public override void SetValue(object component, object value)
{
throw new NotSupportedException();
}
public override void ResetValue(object component)
{
throw new NotSupportedException();
}
public override bool CanResetValue(object component)
{
return false;
}
public override Type ComponentType
{
get { return tail.ComponentType; }
}
public override bool ShouldSerializeValue(object component)
{
return false;
}
}
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
var data = new BookList {
new BookDetails { Title = "abc", TotalRating = 3, Occurrence = 2, Rating = new List<int> {1,2,1}},
new BookDetails { Title = "def", TotalRating = 3, Occurrence = 2, Rating = null },
new BookDetails { Title = "ghi", TotalRating = 3, Occurrence = 2, Rating = new List<int> {3, 2}},
new BookDetails { Title = "jkl", TotalRating = 3, Occurrence = 2, Rating = new List<int>()},
};
Application.Run(new Form
{
Controls = {
new DataGridView {
Dock = DockStyle.Fill,
DataSource = data
}
}
});
}
}
使用系统;
使用系统集合;
使用System.Collections.Generic;
使用系统组件模型;
使用System.Linq;
使用System.Windows.Forms;
公共类书籍详情
{
公共字符串标题{get;set;}
公共整数总体评级{get;set;}
公共整数出现{get;set;}
公共列表评级{get;set;}
}
类图书列表:列表,ITypedList
{
公共PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[]listAccessors)
{
var origProps=TypeDescriptor.GetProperties(typeof(BookDetails));
List newProps=新列表(origProps.Count);
PropertyDescriptor doThisLast=null;
foreach(origProps中的PropertyDescriptor道具)
{
如果(道具名称==“评级”)doThisLast=道具;
else newProps.Add(prop);
}
if(doThisLast!=null)
{
var max=(来自本手册)
让评级=账面评级
其中额定值!=null
选择(int?)额定值.Count).Max()??0;
如果(最大值>0)
{
//希望它可以为Null以解释锯齿数组
Type propType=typeof(int?)//也可以从
//一般情况下,但要使其为空
对于(int i=0;i return(list==null | | list.Count您必须实现一个TypeDescriptor(或者可能是TypeConverter)对于类型。如果你知道该怎么做,那就很简单了。不幸的是,我写的一个很好的例子正在工作中。@leppie-TypeConverter不适用于这里;实际上,ITypedList可能是最简单的;其次是-TypeDescriptionProvider(因为它不会对类型列表使用ICustomTypeDescriptor)@leppie——我们一定是我所知道的唯一两个愚蠢的人,他们疯了,把框架的这个黑暗角落搞得一团糟;p(你也可以让它读写,但这会有点乱,因为你需要知道每行列表的长度;有点痛…@Marc Gravell:Heh:)IIRC,我的解决方案只是继承了BindingSource
。似乎与您的答案相同。(我还忽略了整个“列表访问器”的事情-如果您需要,它只是通过链跟踪每个调用的一种情况)+1很酷,比我想象的要复杂一些,因为列表中的项目数量可变:)这个解决方案非常有趣。我将不得不研究ITypedList和整个PropertyDescriptor/TypeDescriptor字段。我正在调试程序中运行代码,学到了很多东西,非常感谢:)非常有用和简洁。谢谢。这是一个很好的解决方案,它提供了一个很好的例子。谢谢。