ScrollViewer的视口高度VS实际高度

时间:2011-04-20 16:42:58

标签: wpf listbox virtualization scrollviewer actualheight

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

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

如果CanContentScroll为true,则ExtentHeight,ScrollableHeight,ViewportHeight和VerticalOffset属性的值是项目数。如果CanContentScroll为false,则这些属性的值为Device Independent Pixels。

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

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

为什么会出现这种差异?

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

有人可以解释两个列表框之间有什么区别吗?对于Listbox 1

,我需要ViewPort hieght

3 个答案:

答案 0 :(得分:12)

ActualHeight是ScrollViewer的实际高度。视口是ScrollViewers内容中可见的内容。因此,要回答您的问题:ViewportHeightActualHeight不同,如果滚动条的Height可以看到水平滚动条。

所以,总结一下:

ActualHeight = ViewportHeight + HorizontalScrollbarHeight

答案 1 :(得分:9)

最后这是根本原因:

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

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

物理滚动

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

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

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

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

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

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

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

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

在他们之间进行选择

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

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

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

使逻辑滚动滚动条更稳定

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

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

  • 子类跟踪替换 计算拇指位置和 MeasureOverride中的大小与您自己的
  • 更改使用的ScrollBar模板
    用于逻辑滚动的ScrollBar
    改为使用您的子类轨道 常规的

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

  • 更改要使用的ListBox模板 明确设置您的自定义
    上的ScrollViewer模板 它创建的ScrollViewer

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

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

礼貌 - Physical Scrolling vs Logical Scrolling

答案 2 :(得分:1)

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

来自MSDN

  

之间存在差异   高度和宽度的属性   ActualHeight和ActualWidth。对于   例如,ActualHeight属性是   基于其他的计算值   高度输入和布局系统。   该值由布局系统设置   本身,基于实际渲染   通过,因此可能略有滞后   在属性的设定值后面,   比如高度,这是基础   输入变化。

     

因为ActualHeight   是一个计算值,你应该是   意识到可能有多个或   增量报告的变化为   由各种操作的结果   布局系统。布局系统可以   计算所需的测量空间   对于子元素,由约束   父元素,等等。