Вы можете выставлять свойства из INestedObj
для привязки, но решение это очень грязное. какие свойства существуют на объектах, к которым они привязаны. С помощью TypeDescriptionProvider
и CustomTypeDescriptor
вы можете переопределить поведение по умолчанию и, следовательно, добавить / скрыть свойства - в этом случае скрыть свойство NestedObj
и заменить его всеми свойствами вложенного типа.
Техника, которую я собираюсь показать, имеет 2 (большие) предостережения:
- Поскольку вы работаете с интерфейсами (а не с конкретными классами), вы должны добавить дескриптор пользовательского типа во время выполнения.
- Пользовательский дескриптор типа должен иметь возможность создавать конкретный экземпляр
IParentObj
, поэтому он должен знать один такой класс, который имеет конструктор по умолчанию.
(прошу прощения за длинный код)
Во-первых, вам нужно обернуть PropertyDescriptor
из вложенного типа, чтобы к нему можно было обращаться из родительского типа:
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
);
}
}
Затем вам нужно написать собственный дескриптор типа, который предоставляет свойства из вложенного типа:
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;
}
}
... и тогда вам нужен способ раскрытия дескриптора сверху:
public class ParentObjDescriptionProvider : TypeDescriptionProvider {
public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType,
object instance) {
return new ParentObjDescriptor();
}
}
Наконец, во время выполнения (перед привязкой к DataGridView
) необходимо связать поставщика описания типа с интерфейсом IParentObj
. Вы не можете сделать это во время компиляции, потому что TypeDescriptionProviderAttribute
нельзя разместить на интерфейсах ...
TypeDescriptor.AddProvider(new ParentObjDescriptionProvider(), typeof(IParentObj));
Я проверил это, связав DataGridView
с IParentObj[]
и, низко и вот, он создает столбцы для Prop1
, Prop2
и NestedObj.Prop3
.
Вы должны спросить себя, хотя ... это действительно стоит всех этих усилий?