寻找解决方案,使DataGridView控件无法绑定到分层(OO)数据

Mic*_*l J 5 data-binding controls winforms

看起来DataGridView控件只能绑定到平面的数据源(所有属性都是主要类型).我的数据是分层的.例如:

    interface INestedObj
{
    string Prop3 { get; }
}

interface IParentObj
{
    public string Prop1 { get; }
    public string Prop2 { get; }
    public INestedObj NestedObj { get; }
}
Run Code Online (Sandbox Code Playgroud)

鉴于此,如何绑定到实现IParentObj的对象?最终你面临着必须做这样的事情:

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
Run Code Online (Sandbox Code Playgroud)

我正在寻找建议和/或解决方法.

TIA

Bra*_*ith 7

可以INestedObj绑定中公开属性,但解决方案非常混乱.为了给出一些背景知识,所有支持数据绑定的WinForms控件都TypeDescriptor用于确定它们绑定到的对象上存在哪些属性.通过TypeDescriptionProviderCustomTypeDescriptor,您可以覆盖默认行为,从而添加/隐藏属性 - 在这种情况下,隐藏NestedObj属性并将其替换为嵌套类型上的所有属性.

我要展示的技术有2个(大))警告:

  1. 由于您正在使用接口(而不是具体类),因此必须在运行时添加自定义类型描述符.
  2. 自定义类型描述符需要能够创建一个具体的实例IParentObj,因此它必须知道一个具有默认构造函数的类.

(请原谅冗长的代码)

首先,您需要一种PropertyDescriptor从嵌套类型中包装a的方法,以便可以从父类型访问它:

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
        );
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,您需要编写一个自定义类型描述符,从嵌套类型公开属性:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

...然后你需要一种从上面暴露描述符的方法:

public class ParentObjDescriptionProvider : TypeDescriptionProvider {
    public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, 
        object instance) {
        return new ParentObjDescriptor();
    }
}
Run Code Online (Sandbox Code Playgroud)

最后,在运行时(绑定到之前DataGridView),必须将类型描述提供程序与IParentObj接口关联.你不能在编译时这样做,因为TypeDescriptionProviderAttribute不能放在接口上......

TypeDescriptor.AddProvider(new ParentObjDescriptionProvider(), typeof(IParentObj));
Run Code Online (Sandbox Code Playgroud)

我通过将a绑定DataGridView到a 来测试它,IParentObj[]并且低,并且它会创建列Prop1,Prop2以及NestedObj.Prop3.

你必须问自己,但是......真的值得所有的努力吗?


Mic*_*l J 3

这是我在漫长的一天结束后想到的一个简单的解决方案。

我使用 Linq 查询和投影来创建一个匿名类型,该类型在 DataGridView 中显示正确的信息。

var query = from pt in parentObjCollection
  select new {Prop1=pt.Prop1, Prop2=pt.Prop2, NestedObj.Prop3=pt.NestedObj.Prop3};
Run Code Online (Sandbox Code Playgroud)

我必须向 DataPropertyName 属性提供正确的值 (NestedObj.Prop3) 才能获取要在网格中显示的值。

当我有更多时间时,我将尝试实施布拉德利的解决方案。