You can expose properties from INestedObj
for binding, but the solution is very messy.To give some background, all WinForms controls which support databinding use TypeDescriptor
to determine which properties exist on the objects they're binding to. Through TypeDescriptionProvider
and CustomTypeDescriptor
, you can override the default behaviour and thusly add/hide properties - in this case, hiding the NestedObj
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:
- Since you're working with interfaces (and not concrete classes), you have to add the custom type descriptor at runtime.
- The custom type descriptor needs to be able to create a concrete instance of
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:
public class InnerPropertyDescriptor : PropertyDescriptor {
private PropertyDescriptor innerDescriptor;
public InnerPropertyDescriptor(PropertyDescriptor owner,
PropertyDescriptor innerDescriptor, Attribute[] attributes)
: base(owner.Name + "." + innerDescriptor.Name, attributes) {
this.innerDescriptor = innerDescriptor;
}
public override bool CanResetValue(object component) {
return innerDescriptor.CanResetValue(((IParentObj)component).NestedObj);
}
public override Type ComponentType {
get { return innerDescriptor.ComponentType; }
}
public override object GetValue(object component) {
return innerDescriptor.GetValue(((IParentObj)component).NestedObj);
}
public override bool IsReadOnly {
get { return innerDescriptor.IsReadOnly; }
}
public override Type PropertyType {
get { return innerDescriptor.PropertyType; }
}
public override void ResetValue(object component) {
innerDescriptor.ResetValue(((IParentObj)component).NestedObj);
}
public override void SetValue(object component, object value) {
innerDescriptor.SetValue(((IParentObj)component).NestedObj, value);
}
public override bool ShouldSerializeValue(object component) {
return innerDescriptor.ShouldSerializeValue(
((IParentObj)component).NestedObj
);
}
}
Then you need to write a custom type descriptor that exposes the properties from the nested type:
public class ParentObjDescriptor : CustomTypeDescriptor {
public override PropertyDescriptorCollection GetProperties(
Attribute[] attributes) {
PropertyDescriptorCollection properties
= new PropertyDescriptorCollection(null);
foreach (PropertyDescriptor outer in TypeDescriptor.GetProperties(
new ParentObj() /* concrete implementation of IParentObj */,
attributes, true)) {
if (outer.PropertyType == typeof(INestedObj)) {
foreach (PropertyDescriptor inner in TypeDescriptor.GetProperties(
typeof(INestedObj))) {
properties.Add(new InnerPropertyDescriptor(outer,
inner, attributes));
}
}
else {
properties.Add(outer);
}
}
return properties;
}
}
...and then you need a way of exposing the descriptor from above:
public class ParentObjDescriptionProvider : TypeDescriptionProvider {
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType,
object instance) {
return new ParentObjDescriptor();
}
}
Finally, at run-time (before you bind to the DataGridView
), you must associate the type description provider with the IParentObj
interface. You can't do this at compile-time because TypeDescriptionProviderAttribute
can't be placed on interfaces...
TypeDescriptor.AddProvider(new ParentObjDescriptionProvider(), typeof(IParentObj));
I tested this by binding a DataGridView
to an IParentObj[]
and, low and behold, it creates columns for Prop1
, Prop2
and NestedObj.Prop3
.
You have to ask yourself, though... is it really worth all that effort?