我使用来自CodePlex的一些想法以及Bea Stollnitz和 Vincent Da Ven Berhge 的论文(相同链接)的博客实现了一个数据虚拟化解决方案。但是我需要一种不同的方法,所以我决定编写自己的解决方案。
我正在使用DataGrid
这个解决方案来显示大约一百万行。我也在使用 UI 虚拟化。我的解决方案是可行的,但是在某些情况下我遇到了一些奇怪的行为,即如何DataGrid
从源请求数据。
关于解决方案
我最终写了一个清单来完成所有繁重的工作。它是一个名为VirtualList<T>.
It实现ICollectionViewFactory
接口的泛型类,因此集合视图创建机制可以创建一个VirtualListCollectionView<T>
实例来包装它。这个类继承自ListCollectionView
. 我没有按照建议编写自己的ICollectionView
实现。继承似乎也可以正常工作。
将VirtualList<T>
整个数据拆分为页面。它获取总项目数,并且每次DataGrid
通过列表索引器请求行时,它都会加载适当的页面或从缓存中返回它。这些页面在内部回收,并DispatcherTimer
在空闲时间处理未使用的页面。
数据请求模式
我学到的第一件事,
VirtualList<T>
应该实现IList
(非通用)。否则,ItemsControl
会将其视为一个IEnumerable
并查询/枚举所有行。这是合乎逻辑的,因为DataGrid
它不是类型安全的,所以它不能使用IList<T>
接口。索引为 0 的行经常被
DataGrid
. 它似乎用于视觉项目测量(根据调用堆栈)。所以,我只是缓存这个。内部的缓存机制
DataGrid
使用可预测的模式来查询它显示的行。首先它从上到下询问可见行(每行两次),然后在可见区域(包括第一个可见行)之前以降序查询几行(取决于可见区域的大小)顺序如此,从下到上。之后,它从上到下在可见行(包括最后一个可见行)之后请求相同数量的行。如果可见行索引是 4,5,6。数据请求将是:4,4,5,5,6,6,4,3,2,1,6,7,8,9。
如果我的页面大小设置正确,我可以从当前和以前加载的页面中处理所有这些请求。
如果
CanSelectMultipleItems
是True
并且用户使用 SHIFT 按钮或鼠标拖动选择了多个项目,则DataGrid
枚举从列表开头到选择结尾的所有行。IEnumerable
无论是否实现,此枚举都通过接口发生IList
。如果所选行不可见并且当前可见区域与所选行“远”,则有时 DataGrid 会开始请求从所选行到可见区域末尾的所有项目。包括其间的所有行,甚至不可见。我无法弄清楚这种行为的确切模式。也许我的实现是原因。
我的问题
我想知道,为什么
DataGrid
对不可见行的请求,因为这些行在变得可见时会再次被请求?为什么需要每行请求两三次?
谁能告诉我如何使 DataGrid 不使用
IEnumerable
,除了关闭多项选择?