分页场景下,需哪些接口让DataGrid自动发现Schema生成列?
解决WPF DataGrid分页时AutoGenerateColumns失效及虚拟化选中异常问题
嗨,我来帮你搞定这两个问题,同时解释你遇到的行虚拟化选中异常:
一、DataGrid自动生成列需要实现什么接口?
要让DataGrid自动识别数据源的Schema并生成列,你的自定义集合类必须实现 ITypedList 接口。这也是DataTable和DataView能让AutoGenerateColumns正常工作的核心原因——它们通过这个接口把数据列的元信息暴露给DataGrid。
补充一句:如果是强类型对象的集合,DataGrid会直接通过TypeDescriptor.GetProperties()读取对象的公共属性来生成列,但DataTable这种动态列结构的数据源,必须靠ITypedList来动态提供列对应的PropertyDescriptor集合。
二、怎么让你的PagingCollectionView被DataGrid识别Schema?
既然你的底层数据源是DataTable,咱们完全可以直接复用它的PropertyDescriptor集合,不用自己手动构造,既省事儿又不会出错。具体步骤如下:
1. 给PagingCollectionView添加ITypedList实现
修改你的分页集合类,让它继承CollectionView的同时实现ITypedList,并持有底层DataTable的引用:
public class PagingCollectionView : CollectionView, ITypedList { private readonly DataTable _sourceDataTable; // 构造时传入你的数据源DataTable public PagingCollectionView(DataTable sourceTable) : base(sourceTable.DefaultView) { _sourceDataTable = sourceTable; // 这里可以初始化你的分页参数,比如PageSize等 PageSize = 50; } // 实现ITypedList的GetItemProperties方法 public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors) { // 直接复用DataView的GetItemProperties逻辑,和原生DataTable绑定的行为完全一致 return ((ITypedList)_sourceDataTable.DefaultView).GetItemProperties(listAccessors); } // 实现ITypedList的GetListName方法 public string GetListName(PropertyDescriptor[] listAccessors) { // 直接返回DataView的列表名称即可 return ((ITypedList)_sourceDataTable.DefaultView).GetListName(listAccessors); } // 你的分页逻辑实现,比如当前页索引、每页行数、切换页的方法等 public int PageSize { get; set; } public int CurrentPage { get; private set; } // ... 其他分页相关代码 }
2. 绑定到DataGrid
现在把这个分页视图绑定给DataGrid,开启AutoGenerateColumns="True",就能看到DataGrid自动生成列了:
// 初始化你的DataTable后 var pagedView = new PagingCollectionView(DT); DG.ItemsSource = pagedView; DG.AutoGenerateColumns = true;
三、关于你MCVE里的行虚拟化选中问题
你遇到的这个选中状态异常是WPF DataGrid行虚拟化的经典坑:
- 开启行虚拟化后,DataGrid只会为可视区域内的行创建
DataGridRow容器,滚动时,不可见的行容器会被回收复用。 - 你用的是
TwoWay绑定IsSelected,当容器被回收时,它的选中状态可能没有和数据源同步;或者复用旧容器时,绑定没有及时更新到新数据源的状态,导致视觉上的选中状态和实际数据源不一致。
临时修复方案(如果暂时不想用分页)
如果不想立刻切换到分页,可以试试这两个办法:
- 把绑定模式改成OneWay:因为你是通过代码直接修改数据源的
IsSelected列,不需要UI反向同步,所以修改RowStyle:<Setter Property="IsSelected" Value="{Binding IsSelected, Mode=OneWay}" /> - 禁用行虚拟化:这是最直接的办法,但大数据量下会影响加载速度,也就是你已经试过的方案。
不过从长期来看,你的分页方案更适合处理大数据量,结合上面实现的ITypedList接口,就能同时解决自动列生成和性能问题啦。
内容的提问来源于stack exchange,提问作者dotNET
相关产品推荐
相关产品推荐

