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

时间:2010-11-17 21:03:19

标签: winforms data-binding controls

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

    interface INestedObj
{
    string Prop3 { get; }
}

interface IParentObj
{
    public string Prop1 { get; }
    public string Prop2 { get; }
    public INestedObj NestedObj { get; }
}

鉴于此,如何绑定到实现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

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

TIA

4 个答案:

答案 0 :(得分:7)

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

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

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

    首先,您需要一种从嵌套类型中包装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[]Prop1Prop2创建列。< / p>

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

答案 1 :(得分:3)

这是一个简单的解决方案,在漫长的一天结束时来到我身边。

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

var query = from pt in parentObjCollection
  select new {Prop1=pt.Prop1, Prop2=pt.Prop2, NestedObj.Prop3=pt.NestedObj.Prop3};

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

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

答案 2 :(得分:2)

您可以为“NestedObj.Prop3”添加未绑定列并手动处理其值。要填充列,请处理DataGridView的CellFormatting事件,从当前行获取DataBoundItem并从中获取Prop3。要更新数据源,请处理CellValidated事件以更新DataBoundItem。

可能有比我提到的更合适的事件,但你明白了。

答案 3 :(得分:0)

我发现最简单的方法是创建一个Self属性。看到这个解决方案 Databinding a combobox column to a datagridview per row (not the entire column)