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?