It would seem that the DataGridView control can only bind to data sources that are flat (all the Properties are primative types). My data is hierarchal. For example:
interface INestedObj
{
string Prop3 { get; }
}
interface IParentObj
{
public string Prop1 { get; }
public string Prop2 { get; }
public INestedObj NestedObj { get; }
}
Given this, how does one bind to an object implementing IParentObj? Eventually you are faced with having to do something like this:
grid.Columns["prop1Col"].DataPropertyName = "Prop1";
grid.Columns["prop2Col"].DataPropertyName = "Prop2";
grid.Columns["prop3Col"].DataPropertyName = "How to display Prop3?";
grid.Columns["prop3Col"].DataPropertyName = "NestedObj.Prop3"; // does not work
I am looking for advice and/or work-arounds.
TIA
You could probably add an unbound column for "NestedObj.Prop3" and manually handle its value. To get the column populated, handle the CellFormatting event of the DataGridView, get the DataBoundItem from the current row and get the Prop3 from that. To update the data source, handle the CellValidated event to update the DataBoundItem.
There may be more appropriate events to use than the ones I mentioned, but you get the idea.
The easiest way I found is to create a Self property. See this solution: Databinding a combobox column to a datagridview per row (not the entire column)
You can expose properties from
INestedObj
for binding, but the solution is very messy.To give some background, all WinForms controls which support databinding useTypeDescriptor
to determine which properties exist on the objects they're binding to. ThroughTypeDescriptionProvider
andCustomTypeDescriptor
, you can override the default behaviour and thusly add/hide properties - in this case, hiding theNestedObj
property and replacing it with all of the properties on the nested type.The technique i'm going to show has 2 (big-ish) caveats:
IParentObj
, therefore it must know one such class which has a default constructor.(Please excuse the lengthy code)
First, you need a way of wrapping a
PropertyDescriptor
from the nested type so that it can be accessed from the parent type:Then you need to write a custom type descriptor that exposes the properties from the nested type:
...and then you need a way of exposing the descriptor from above:
Finally, at run-time (before you bind to the
DataGridView
), you must associate the type description provider with theIParentObj
interface. You can't do this at compile-time becauseTypeDescriptionProviderAttribute
can't be placed on interfaces...I tested this by binding a
DataGridView
to anIParentObj[]
and, low and behold, it creates columns forProp1
,Prop2
andNestedObj.Prop3
.You have to ask yourself, though... is it really worth all that effort?
Here is a simple solution that came to me at the end of a long day.
I used a Linq query and projection to create an anonymous type that displays the proper information in the DataGridView.
I had to supply the proper value (NestedObj.Prop3) to the DataPropertyName property to get the value to display in the grid.
When I have more time I am going to try and implement Bradley's solution.