我使用CodePlex和Bea Stollnitz博客以及Vincent Da Ven Berhge的论文(同一链接)中的一些想法实施了数据虚拟化解决方案。但是我需要一种不同的方法,所以我决定编写自己的解决方案。
我使用DataGrid
使用此解决方案显示大约一百万行。我也在使用UI虚拟化。我的解决方案是可行的,但在某些情况下我会遇到DataGrid
如何从其来源请求数据的奇怪行为。
关于解决方案
我最终写了一份清单,完成了所有繁重的工作。它是一个名为VirtualList<T>.
的泛型类,它实现了ICollectionViewFactory
接口,因此集合视图创建机制可以创建一个VirtualListCollectionView<T>
实例来包装它。该类继承自ListCollectionView
。我没有按照建议编写我自己的ICollectionView
实现。继承似乎也很好。
VirtualList<T>
将整个数据拆分为多个页面。它获取总项数,每次DataGrid
通过列表索引器请求行时,它会加载相应的页面或从缓存中返回它。页面在内部回收,DispatcherTimer
在空闲时间内处理未使用的页面。
数据请求模式
我学到的第一件事是,VirtualList<T>
应该实现IList
(非通用)。否则,ItemsControl
会将其视为IEnumerable
并查询/枚举所有行。这是合乎逻辑的,因为DataGrid
不是类型安全的,所以它不能使用IList<T>
接口。
DataGrid
经常询问0索引的行。它似乎用于视觉项目测量(根据调用堆栈)。所以,我只是缓存这个。
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
,除了关闭多个项目选择吗?