简体   繁体   中英

How does DataGridView get the list of properties in a class?

When you set a List or, even better, a BindingList as the source of a DataGridView, it gets the list of properties and uses them to generate columns.

If I do the following:

        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;

Nothing happens. No columns or rows are ever added to the DataGridView. I'm guessing that's because there is a method missing that allows the DataGridView to get the collection of properties.

If I run the same code but replace ExpandoObject with MyCustomClass in the code sample above, as defined below, the DataGridView would populate just fine.

public class MyCustomClass
{
    public string key { get; set; }
    public string name { get; set; }
    public string number { get; set; }
}

However, since I'm reading my data from a source that may change, I need to use a dynamic class. I've tried a number of methods including a BindingList BindigList> none of which will bind the dynamic members to column. I've even tried creating a class that inherits DynamicObject with overrides for TryGetMember and TrySetMember.

I feel like I'm spinning my wheels and overlooking a simple solution. Maybe there's some trick using a Dictionary and Linq that I'm missing here.

Caveats: I'm collecting data, summarizing it and displaying it. I don't need to add or remove data once it's been displayed. I will be trying to filter and sort it though. I'll cross that bridge, though, once I figure out how to show it.

My example shows simple known values and types, but my actual source data will be less predictable and it is parsed from JSON using Newtonsoft JSON with a structure as follows:

[ { "match_number": 1, "set_number": 1, "key": "2017onbar_f1m1", "score_breakdown": { "blue": { "totalPoints": 236, "foulCount": 1, "adjustPoints": 0, "overtime": true }, "red": { "totalPoints": 236, "foulCount": 1, "adjustPoints": 0, "overtime": true } }, "teammembers": { "blue": { "teams": [ "member1", "member2", "member3" ] }, "red": { "teams": [ "member4", "member5", "member6" ] } } }]

However, the score_breakdown fields will vary.

Try converting your BindingList into 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;

    }

In case when given datasource is instance of some type (not instance of Type ) DatagridView.DataSource setter will use source.GetType().GetProperties() for retrieving properties, which will be used for generating columns.

Problem is that in your case type ExpandoObject will return empty collection

dynamic ds = new ExpandoObject();
ds.key = "0000";
ds.Name = "Bob";
ds.Number = "2255442";

ds.GetType().GetProperties() // return empty collection

Because you know name of properties in compile time you can use new features of C# and create tuple

var ds = ( key: "0000", Name: "Bob", Number: "2255442" );

But again - if you know properties names at compile time why not create a class for it?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM