【问题标题】:How does DataGridView get the list of properties in a class?DataGridView如何获取类中的属性列表?
【发布时间】:2017-04-14 05:54:20
【问题描述】:

当您将 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; }
}

但是,由于我正在从可能发生变化的源中读取数据,因此我需要使用动态类。我尝试了许多方法,包括 BindingList BindigList>,这些方法都不会将动态成员绑定到列。我什至尝试创建一个继承 DynamicObject 并覆盖 TryGetMember 和 TrySetMember 的类。

我觉得我在旋转我的轮子,却忽略了一个简单的解决方案。也许我在这里缺少使用 Dictionary 和 Linq 的一些技巧。

注意事项:我正在收集数据、对其进行总结和展示。一旦显示数据,我就不需要添加或删除数据。不过,我将尝试对其进行过滤和排序。不过,一旦我弄清楚如何展示它,我就会越过那座桥。

我的示例显示了简单的已知值和类型,但我的实际源数据将不太可预测,它是使用 Newtonsoft JSON 从 JSON 解析的,其结构如下:

[ { “匹配号码”:1, "set_number": 1, "key": "2017onbar_f1m1", “分数分解”:{ “蓝色”: { “总点数”:236, “犯规”:1, “调整点”:0, “加班”:是的 }, “红色的”: { “总点数”:236, “犯规”:1, “调整点”:0, “加班”:是的 } }, “团队成员”: { “蓝色”: { “团队”:[ “成员1”, "成员2", “成员3” ] }, “红色的”: { “团队”:[ "成员4", "会员5", “会员6” ] } } }]

但是,score_breakdown 字段会有所不同。

【问题讨论】:

  • dynamic 用于datagridview 的目的是什么? DataGridView.DataSource 将收集任何其他强类型对象。
  • 好像是 XY 问题,你能说明你从那个来源得到什么样的数据(格式)吗?
  • 源数据有点大,但是源是使用Newtonsoft.JSON转换的JSON。有些结构是不变的,但有些会有所不同。

标签: c# datagridview dynamicobject


【解决方案1】:

尝试将您的 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;

    }

【讨论】:

  • 我会试一试的。谢谢。
  • 谢谢@Jury_Golubev。我认为您将我推向了与我计划不同的方向,但我认为这个选项最终会更好地工作。考虑到我正在尝试做的事情,使用 ExpandoObject 可能是错误的方法,我认为最好使用字典列表以及将它们转换为将其表示为 DataTable 的方法。干净多了。谢谢!
【解决方案2】:

如果给定的数据源是某种类型的实例(不是Type 的实例),DatagridView.DataSource setter 将使用source.GetType().GetProperties() 检索属性,这些属性将用于生成列。

问题是在您的情况下,类型 ExpandoObject 将返回空集合

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" );

但同样 - 如果您在编译时知道属性名称,为什么不为其创建一个类?

【讨论】:

  • 在我提供的相同代码中,我只是添加了一些示例数据,但在我的实际应用程序中,我从 JSON 对象中解析数据,因此属性未知直到运行时间。我确实尝试使用内部字典和自定义 GetPropoerties 方法创建一个类,但这似乎对我不起作用,可能是因为我错误地格式化了 Ilist
猜你喜欢
  • 2020-02-29
  • 2010-10-18
  • 2021-03-26
  • 1970-01-01
  • 1970-01-01
  • 2013-11-06
  • 1970-01-01
  • 2014-02-27
相关资源
最近更新 更多