Winforms 正在寻找DataGridView控件无法绑定到分层(OO)数据的解决方法
DataGridView控件似乎只能绑定到平面数据源(所有属性都是primative类型)。我的数据是分层的。例如:Winforms 正在寻找DataGridView控件无法绑定到分层(OO)数据的解决方法,winforms,data-binding,controls,Winforms,Data Binding,Controls,DataGridView控件似乎只能绑定到平面数据源(所有属性都是primative类型)。我的数据是分层的。例如: interface INestedObj { string Prop3 { get; } } interface IParentObj { public string Prop1 { get; } public string Prop2 { get; } public INestedObj NestedObj { get; } } 鉴于
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您可能可以为“NestedObj.Prop3”添加一个未绑定的列并手动处理其值。要填充列,请处理DataGridView的CellFormatting事件,从当前行获取DataBoundItem,然后从当前行获取Prop3。要更新数据源,请处理CellValidated事件以更新DataBoundItem 可能有比我提到的事件更合适的事件可供使用,但你明白了。你可以从
INestedObj
中公开属性进行绑定,但解决方案非常凌乱,所有支持数据绑定的WinForms控件都使用TypeDescriptor
来确定它们绑定到的对象上存在哪些属性。通过TypeDescriptionProvider
和CustomTypeDescriptor
,您可以覆盖默认行为,从而添加/隐藏属性-在这种情况下,隐藏NestedObj
属性并将其替换为嵌套类型上的所有属性
我要展示的技术有两个(大的)警告:
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
创建了列
不过你得问问自己。。。这真的值得所有这些努力吗?在漫长的一天结束后,我想到了一个简单的解决方案 我使用Linq查询和投影创建了一个匿名类型,在DataGridView中显示正确的信息
var query = from pt in parentObjCollection
select new {Prop1=pt.Prop1, Prop2=pt.Prop2, NestedObj.Prop3=pt.NestedObj.Prop3};
我必须为DataPropertyName属性提供适当的值(NestedObj.Prop3),以获得要在网格中显示的值
当我有更多的时间时,我将尝试实施Bradley的解决方案。我发现的最简单的方法是创建一个Self属性。请参阅此解决方案:
哇,谢谢你,布拉德利。你得到了完整性奖杯。昨晚我找到了一个不同的(更简单?)解决方案,我现在就发布。这是一个非常优雅的解决方案,但是你必须牺牲一些东西,比如更改通知(如果你的对象首先支持它,我不知道)。最后一件您可能想了解的事情是
ITypedList
接口,集合可以实现该接口以公开自定义属性。经过反思,它比我发布的TypeDescriptorProvider
解决方案略显粗糙。