DataGridView如何获取class中的属性列表?
How does DataGridView get the list of properties in a class?
当您将 List 或更好的 BindingList 设置为 DataGridView 的源时,它会获取属性列表并使用它们生成列。
如果我执行以下操作:
StatsList = new BindingList<ExpandoObject>();
// Add seed record to generate columns in DataGridView
dynamic ds = new ExpandoObject();
ds.key = "0000";
ds.Name = "Bob";
ds.Number = "2255442";
ds.key = "0001";
ds.Name = "Nathan";
ds.Number = "1217479";
StatsList.Add(ds);
dgvListView.DataSource = StatsList;
没有任何反应。没有列或行被添加到 DataGridView。我猜这是因为缺少一种允许 DataGridView 获取属性集合的方法。
如果我 运行 相同的代码,但在上面的代码示例中将 ExpandoObject 替换为 MyCustomClass,如下定义,DataGridView 会很好地填充。
public class MyCustomClass
{
public string key { get; set; }
public string name { get; set; }
public string number { get; set; }
}
但是,由于我正在从可能发生变化的来源读取数据,因此我需要使用动态 class。我尝试了多种方法,包括 BindingList BindigList> none ,其中将动态成员绑定到列。我什至尝试创建一个 class 继承 DynamicObject 并覆盖 TryGetMember 和 TrySetMember。
我感觉自己在空转而忽略了一个简单的解决方案。也许这里缺少一些使用字典和 Linq 的技巧。
注意事项:我正在收集数据,对其进行总结并进行展示。显示后我不需要添加或删除数据。不过,我将尝试对其进行过滤和排序。不过,一旦我弄清楚如何展示它,我就会穿过那座桥。
我的示例显示了简单的已知值和类型,但我的实际源数据将难以预测,它是使用 Newtonsoft JSON 从 JSON 解析的,结构如下:
[
{
"match_number": 1,
"set_number": 1,
"key": "2017onbar_f1m1",
"score_breakdown":{
"blue":{
"totalPoints": 236,
"foulCount": 1,
"adjustPoints": 0,
"overtime":真
},
"red":{
"totalPoints": 236,
"foulCount": 1,
"adjustPoints": 0,
"overtime":真
}
},
"teammembers":{
"blue":{
"teams": [
"member1",
"member2",
"member3"
]
},
"red":{
"teams": [
"member4",
"member5",
"member6"
]
}
}
}]
但是,score_breakdown 字段会有所不同。
尝试将 BindingList 转换为 DataTable
protected void Page_Load(object sender, EventArgs e)
{
var StatsList = new BindingList<ExpandoObject>();
// Add seed record to generate columns in DataGridView
dynamic ds = new ExpandoObject();
ds.key = "0000";
ds.Name = "Bob";
ds.Number = "2255442";
dynamic ds2 = new ExpandoObject();
ds2.key = "0001";
ds2.Name = "Nathan";
ds2.Number = "1217479";
StatsList.Add(ds);
StatsList.Add(ds2);
GridView1.DataSource = ExpandoListToDataTable(StatsList);
GridView1.DataBind();
}
protected DataTable ExpandoListToDataTable(BindingList<ExpandoObject> d)
{
var dt = new DataTable();
foreach (var a in d)
{
foreach (var key in ((IDictionary<string, object>)a).Keys)
{
if (!dt.Columns.Contains(key))
{
dt.Columns.Add(key);
}
}
dt.Rows.Add(((IDictionary<string, object>)a).Values.ToArray());
}
return dt;
}
如果给定的数据源是某种类型的实例(不是 Type
的实例)DatagridView.DataSource
setter 将使用 source.GetType().GetProperties()
检索属性,这将被使用用于生成列。
问题是在你的情况下类型 ExpandoObject
将 return 清空集合
dynamic ds = new ExpandoObject();
ds.key = "0000";
ds.Name = "Bob";
ds.Number = "2255442";
ds.GetType().GetProperties() // return empty collection
因为您在编译时知道属性名称,所以您可以使用 C# 的新功能并创建元组
var ds = ( key: "0000", Name: "Bob", Number: "2255442" );
但再次强调 - 如果您在编译时知道属性名称,为什么不为它创建一个 class?
当您将 List 或更好的 BindingList 设置为 DataGridView 的源时,它会获取属性列表并使用它们生成列。
如果我执行以下操作:
StatsList = new BindingList<ExpandoObject>();
// Add seed record to generate columns in DataGridView
dynamic ds = new ExpandoObject();
ds.key = "0000";
ds.Name = "Bob";
ds.Number = "2255442";
ds.key = "0001";
ds.Name = "Nathan";
ds.Number = "1217479";
StatsList.Add(ds);
dgvListView.DataSource = StatsList;
没有任何反应。没有列或行被添加到 DataGridView。我猜这是因为缺少一种允许 DataGridView 获取属性集合的方法。
如果我 运行 相同的代码,但在上面的代码示例中将 ExpandoObject 替换为 MyCustomClass,如下定义,DataGridView 会很好地填充。
public class MyCustomClass
{
public string key { get; set; }
public string name { get; set; }
public string number { get; set; }
}
但是,由于我正在从可能发生变化的来源读取数据,因此我需要使用动态 class。我尝试了多种方法,包括 BindingList BindigList> none ,其中将动态成员绑定到列。我什至尝试创建一个 class 继承 DynamicObject 并覆盖 TryGetMember 和 TrySetMember。
我感觉自己在空转而忽略了一个简单的解决方案。也许这里缺少一些使用字典和 Linq 的技巧。
注意事项:我正在收集数据,对其进行总结并进行展示。显示后我不需要添加或删除数据。不过,我将尝试对其进行过滤和排序。不过,一旦我弄清楚如何展示它,我就会穿过那座桥。
我的示例显示了简单的已知值和类型,但我的实际源数据将难以预测,它是使用 Newtonsoft JSON 从 JSON 解析的,结构如下:
[ { "match_number": 1, "set_number": 1, "key": "2017onbar_f1m1", "score_breakdown":{ "blue":{ "totalPoints": 236, "foulCount": 1, "adjustPoints": 0, "overtime":真 }, "red":{ "totalPoints": 236, "foulCount": 1, "adjustPoints": 0, "overtime":真 } }, "teammembers":{ "blue":{ "teams": [ "member1", "member2", "member3" ] }, "red":{ "teams": [ "member4", "member5", "member6" ] } } }]
但是,score_breakdown 字段会有所不同。
尝试将 BindingList 转换为 DataTable
protected void Page_Load(object sender, EventArgs e)
{
var StatsList = new BindingList<ExpandoObject>();
// Add seed record to generate columns in DataGridView
dynamic ds = new ExpandoObject();
ds.key = "0000";
ds.Name = "Bob";
ds.Number = "2255442";
dynamic ds2 = new ExpandoObject();
ds2.key = "0001";
ds2.Name = "Nathan";
ds2.Number = "1217479";
StatsList.Add(ds);
StatsList.Add(ds2);
GridView1.DataSource = ExpandoListToDataTable(StatsList);
GridView1.DataBind();
}
protected DataTable ExpandoListToDataTable(BindingList<ExpandoObject> d)
{
var dt = new DataTable();
foreach (var a in d)
{
foreach (var key in ((IDictionary<string, object>)a).Keys)
{
if (!dt.Columns.Contains(key))
{
dt.Columns.Add(key);
}
}
dt.Rows.Add(((IDictionary<string, object>)a).Values.ToArray());
}
return dt;
}
如果给定的数据源是某种类型的实例(不是 Type
的实例)DatagridView.DataSource
setter 将使用 source.GetType().GetProperties()
检索属性,这将被使用用于生成列。
问题是在你的情况下类型 ExpandoObject
将 return 清空集合
dynamic ds = new ExpandoObject();
ds.key = "0000";
ds.Name = "Bob";
ds.Number = "2255442";
ds.GetType().GetProperties() // return empty collection
因为您在编译时知道属性名称,所以您可以使用 C# 的新功能并创建元组
var ds = ( key: "0000", Name: "Bob", Number: "2255442" );
但再次强调 - 如果您在编译时知道属性名称,为什么不为它创建一个 class?