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;
}