WPF DataGrid:CanContentScroll属性导致奇怪的行为

时间:2010-06-17 14:27:50

标签: wpf binding wpfdatagrid

我有一个解决方案,我根据用户标准生成一个DataGrid(或多个实例)..每个网格通过ObservableCollection继续接收数据

我遇到的问题是,滚动表现得很奇怪。这是不稳定的,滚动条会在滚动时调整自己的大小。

比我发现.. CanContentScroll 属性!它完全修复了奇怪的滚动行为,带给我暂时的幸福和快乐。

然而,它会导致2个不幸的副作用。

  1. 每当我重新创建网格实例并将它们绑定到我的可观察集合时,它会冻结整个窗口5秒钟。当我的网格增长到一个大尺寸时,这种延迟可以持续30秒。

  2. 当我调用TradeGrid.ScrollIntoView(TradeGrid.Items(TradeGrid.Items.Count - 1))滚动到底部时,它会跳到底部而不是回到顶部。

  3. 还有另一种实现平滑滚动的方法吗?

2 个答案:

答案 0 :(得分:37)

您遇到物理滚动和逻辑滚动之间的差异。

正如您所发现的,每个人都有权衡。

物理滚动

物理滚动(CanContentScroll = false)只是以像素为单位,所以:

  • 视口始终表示滚动范围的完全相同部分,为您提供平滑的滚动体验,

  • DataGrid的全部内容必须完全应用所有模板并进行测量和排列,以确定滚动条的大小,从而导致加载过程中的长时间延迟和高RAM使用率,
  • 它并不真正滚动项目,所以它不能很好地理解ScrollIntoView

逻辑滚动

逻辑滚动(CanContentScroll = true)按项而不是像素计算其滚动视口和范围,因此:

  • 视口可能会在不同时间显示不同数量的项目,这意味着视口中的项目数量与范围中的项目数量相比会有所不同,从而导致滚动条长度发生变化,并且

  • 滚动从一个项目移动到下一个项目,从不介于两者之间,导致“生涩”滚动

  • 只要你在底层使用VirtualizingStackPanel,它只需要应用模板并测量和排列目前实际可见的项目,

  • ScrollIntoView要简单得多,因为它只需要将正确的项目索引导入视图

在他们之间进行选择

这是WPF提供的唯一两种滚动。您必须根据上述权衡选择它们。通常,逻辑滚动最适合中型到大型数据集,物理滚动最适合小型数据集。

在物理滚动期间加速加载的一个技巧是使物理滚动更好是将项目包装在具有固定大小的自定义装饰器中,并在不可见时将其子项的可见性设置为隐藏。这可以防止ApplyTemplate,Measure和Arrange在该项的后代控件上发生,直到您准备好它为止。

使物理滚动的ScrollIntoView更可靠的一个技巧是调用它两次:一次在调度程序回调DispatcherPriority.ApplicationIdle中立即执行一次。

使逻辑滚动滚动条更稳定

如果所有项目的高度相同,则视口中可见的项目数量将保持不变,从而导致滚动缩略图大小保持不变(因为如果项目不变,则与总数量的比率保持不变)。

也可以修改ScrollBar本身的行为,因此拇指总是被计算为固定大小。要做到这一点,没有任何hacky代码隐藏:

  • 子类跟踪用您自己的
  • 替换MeasureOverride中Thumb位置和大小的计算
  • 更改用于逻辑滚动ScrollBar的ScrollBar模板,以使用子类轨道而不是常规轨道
  • 更改ScrollViewer模板,在逻辑滚动的ScrollBar上显式设置自定义ScrollBar模板(而不是使用默认模板)
  • 更改ListBox模板以使用在其创建的ScrollViewer上显式设置自定义ScrollViewer模板

这意味着在内置的WPF模板中复制了很多模板代码,因此它不是一个非常优雅的解决方案。但是替代方法是使用hacky代码隐藏等待所有模板扩展,然后找到ScrollBar并将ScrollBar模板替换为使用自定义Track的模板。这段代码以一些非常棘手的代码为代价保存了两个大模板(ListBox,ScrollViewer)。

使用不同的Panel会有更多的工作量:VirtualizingStackPanel是唯一虚拟化的Panel,只有它和StackPanel才能进行逻辑滚动。由于您正在利用VirtualizingStackPanel的虚拟化功能,因此您必须重新实现所有这些以及所有IScrollInfo信息功能以及常规Panel功能。我可以做类似的事情,但我会分配好几天,也许很多天才能做到。我建议你不要尝试。

答案 1 :(得分:2)

我的DataGrid也有同样的问题,最后我做了:

ScrollViewer.CanContentScroll="True"   
EnableRowVirtualization="True"
VirtualizingPanel.VirtualizationMode="Standard"

现在一切都在我的DataGrid中运行良好。