9

两者都是非常笼统的术语,但我很想知道除了我们使用虚拟化的情况之外,这些高度何时会有所不同?

还有一个问题:我在 MSDN 上读到:

如果 CanContentScroll 为 true,则 ExtentHeight、ScrollableHeight、ViewportHeight 和 VerticalOffset 属性的值是项目数。如果 CanContentScroll 为 false,则这些属性的值是与设备无关的像素。

但是,我遇到了 ViewPort Height 的问题:我在应用程序中有 2 个列表框:
1. 启用了虚拟化并且 CanContentScroll = True。
2.没有虚拟化并且CanContentScroll = True。

在 ListBox 1 中,拖放 Viewport Height 达到 4/5(当前可见的元素数)。但是在 ListBox 2 中,我得到的视口高度等于列表框的实际高度。

为什么会有这种差异?

更多发现:
1. 可滚动高度是在滚动查看器中不可见的项目数
2. 视口高度是在滚动查看器中可见的项目数。
因此视口高度 + ScrollableHeight = 范围高度

有人可以解释一下两个列表框有什么区别吗?如果是 Listbox 1,我需要 ViewPort hieght

4

3 回答 3

12

ActualHeight是 ScrollViewer 的实际高度。视口是从 ScrollViewers 内容可见的。所以回答你的问题:ViewportHeightActualHeight水平滚动条是否可以通过滚动条看到不同Height

所以,总结一下:

ActualHeight = ViewportHeight + HorizontalScrollbarHeight
于 2011-04-20T20:05:32.777 回答
9

最后这是根本原因:

引用https://stackoverflow.com/a/3062692/3195477

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

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

物理滚动

物理滚动 (CanContentScroll=false) 只是按像素进行,所以:

视口始终代表滚动范围的完全相同的部分,为您提供流畅的滚动体验,但

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

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

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

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

只要您在后台使用 VirtualizingStackPanel,它只需要应用模板并测量和排列当前实际可见的项目,并且

ScrollIntoView 要简单得多,因为它只需要获取正确的项目索引即可

在他们之间选择

这是 WPF 提供的仅有的两种滚动方式。您必须根据上述权衡在它们之间进行选择。一般来说,逻辑滚动最适合中大型数据集,而物理滚动最适合小型数据集。

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

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

使逻辑滚动滚动条更稳定

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

也可以修改 ScrollBar 本身的行为,以便始终将拇指计算为固定大小。要做到这一点,无需任何 hacky 代码隐藏:

  • 子类 Track 用您自己的替换 MeasureOverride 中的 Thumb 位置和大小的计算

  • 更改用于逻辑滚动 ScrollBar 的 ScrollBar 模板以使用您的子类 Track 而不是常规的

  • 更改 ScrollViewer 模板以在逻辑滚动 ScrollBar 上显式设置您的自定义 ScrollBar 模板(而不是使用默认模板)

  • 更改 ListBox 模板以使用在它创建的 ScrollViewer 上显式设置您的自定义 ScrollViewer 模板

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

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

于 2011-04-25T08:35:42.317 回答
1

它们可以不同于(指定的)Height被评估点到(正在进行的)渲染过程中的任何给定时间。

来自MSDN

Height 和 Width 与 ActualHeight 和 ActualWidth 的属性是有区别的。例如,ActualHeight 属性是基于其他高度输入和布局系统的计算值。该值由布局系统本身根据实际渲染过程设置,因此可能会稍微落后于作为输入更改基础的属性(例如高度)的设置值。

由于 ActualHeight 是一个计算值,因此您应该知道,由于布局系统的各种操作,可能会有多个或增量报告的更改。布局系统可能正在计算子元素所需的度量空间、父元素的约束等。

于 2011-04-20T16:47:15.297 回答