WinForms DataGridView继承类属性绑定问题求助(无法修改ItemSelection)
问题
代码示例
// 对象模型 public class LineItem { public PropA { get; set; } public PropB { get; set; } } public class CustomLineItem : LineItem { public PropC { get; set; } } public class ItemSelection : Collection<LineItem> { } // UI层 ItemSelection items = Service.GetItemsFromDatabase(); // 包含CustomLineItem类型对象 BindingSource bsLineItems = new BindingSource(); bsLineItems.DataSource = items; this.dgvLineItems.AutoGenerateColumns = false; // 列通过设计器添加,已设置所有列的DataPropertyName属性 this.dgvLineItems.DataSource = this.bsLineItems;
问题描述
DataGridView可正常绑定显示PropA和PropB属性,但因继承关系,无法显示CustomLineItem特有的PropC属性。现有项目无法修改ItemSelection类的定义(否则会改为泛型实现ItemSelection<T> where T : LineItem),求其他可行解决方案?
解决方案
1. 利用CellFormatting事件手动赋值
在DataGridView的CellFormatting事件中,判断当前行绑定的对象是否为CustomLineItem,若是则直接给对应列赋值:
private void dgvLineItems_CellFormatting(object sender, DataGridViewCellFormattingEventArgs e) { // 替换成你设计器中对应PropC的列名或索引 if (e.ColumnIndex == dgvLineItems.Columns["colPropC"].Index && e.RowIndex >= 0) { var customItem = dgvLineItems.Rows[e.RowIndex].DataBoundItem as CustomLineItem; if (customItem != null) { e.Value = customItem.PropC; e.FormattingApplied = true; // 标记已处理,避免默认格式化逻辑干扰 } } }
2. 转换数据源类型
如果确定集合中大部分或全部是CustomLineItem,可以直接将数据源转换为该类型的集合再绑定:
// 过滤并转换为CustomLineItem列表 var customItems = items.OfType<CustomLineItem>().ToList(); bsLineItems.DataSource = customItems;
如果集合里同时存在LineItem和CustomLineItem,可以用匿名类型统一包装所有需要的属性:
var wrappedItems = items.Select(item => new { PropA = item.PropA, PropB = item.PropB, PropC = (item as CustomLineItem)?.PropC }).ToList(); bsLineItems.DataSource = wrappedItems;
3. 为列设置自定义绑定
直接给对应PropC的列设置绑定表达式,明确访问子类属性:
// 假设colPropC是设计器中添加的列 colPropC.DataBindings.Add(new Binding("Value", bsLineItems, "PropC", true));
这种方式适用于绑定源中确实存在CustomLineItem对象的场景,DataGridView会自动尝试解析该属性。
4. 用TypeDescriptor扩展属性
通过自定义类型描述器,为LineItem动态添加PropC属性,让DataGridView能识别到子类的属性:
public class LineItemTypeDescriptionProvider : TypeDescriptionProvider { public LineItemTypeDescriptionProvider(TypeDescriptionProvider parent) : base(parent) { } public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) { var baseDescriptor = base.GetTypeDescriptor(objectType, instance); return new LineItemCustomTypeDescriptor(baseDescriptor, instance); } } public class LineItemCustomTypeDescriptor : CustomTypeDescriptor { private readonly object _instance; public LineItemCustomTypeDescriptor(ICustomTypeDescriptor parent, object instance) : base(parent) { _instance = instance; } public override PropertyDescriptorCollection GetProperties() { var baseProps = base.GetProperties(); var propsList = new List<PropertyDescriptor>(baseProps.Cast<PropertyDescriptor>()); if (_instance is CustomLineItem customItem) { // 动态添加PropC属性描述符 var propCDescriptor = TypeDescriptor.CreateProperty( typeof(LineItem), "PropC", customItem.PropC.GetType(), new Attribute[] { new BrowsableAttribute(true) }); propsList.Add(propCDescriptor); } return new PropertyDescriptorCollection(propsList.ToArray()); } }
然后在程序启动时注册该类型描述器:
TypeDescriptor.AddProvider(new LineItemTypeDescriptionProvider(TypeDescriptor.GetProvider(typeof(LineItem))), typeof(LineItem));
这种方式无需修改原有实体类,能让DataGridView自动识别子类属性,适合需要长期支持多子类属性显示的场景。
内容的提问来源于stack exchange,提问作者Santhosh Shanmugam
相关产品推荐
相关产品推荐

