wpf ScrollViewer 的视口高度 VS 实际高度

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5733738/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 22:49:03  来源:igfitidea点击:

ScrollViewer's Viewport Height VS Actual Height

wpflistboxvirtualizationscrollvieweractualheight

提问by Rohit

Both are quite general terms but I'm curious to know when these height will be different apart from the case we're using Virtualization?

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

One more question: I read on MSDN:

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

If CanContentScroll is true, the values of the ExtentHeight, ScrollableHeight, ViewportHeight, and VerticalOffset properties are number of items. If CanContentScroll is false, the values of these properties are Device Independent Pixels.

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

However I'm facing an issue with ViewPort Height: I've 2 listbox in application:
1. Which have Virtualization Enabled and CanContentScroll = True.
2. Which have no virtualization and CanContentScroll = True.

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

In ListBox 1 while drag-drop Viewport Height comes to 4/5 (Number of elements currently visible). However in ListBox 2 i get Viewport Height equal to Actual Height of Listbox.

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

Why this difference?

为什么会有这种差异?

Few more findings:
1. Scrollable Height is number of items not visible in scrollviewer
2. Viewport Height is number of items visible in scrollviewer.
Thus Viewport Height + ScrollableHeight = Extent Height

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

Can someone please explain what's the difference between two listboxes? I need ViewPort hieght in case of Listbox 1

有人可以解释一下两个列表框之间的区别吗?在列表框 1 的情况下,我需要 ViewPort 高度

回答by Markus Hütter

the ActualHeightis the actual height of the ScrollViewer. The Viewport is what is visible from the ScrollViewers Content. So to answer your question: ViewportHeightdiffers from ActualHeightif the horizontal Scrollbar is visible by the Heightof the Scrollbar.

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

so, to sum this up:

所以,总结一下:

ActualHeight = ViewportHeight + HorizontalScrollbarHeight

回答by Rohit

Finally This was the root cause:

最后这是根本原因:

You are encountering the differences between physical scrolling and logical scrolling.

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

As you have discovered, each has its tradeoffs.

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

Physical scrolling

物理滚动

Physical scrolling (CanContentScroll=false) just goes by pixels, so:

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

The viewport always represents exactly the same portion of your scroll extent, giving you a smooth scrolling experience, and but

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

The entire contents of the DataGrid must have all templates fully applied and be measured and arranged to determine the size of the scrollbar, leading to long delays during loading and high RAM usage, and It doesn't really scroll items so it doesn't understand ScrollIntoView very well Logical scrolling

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

Logical scrolling(CanContentScroll=true) calculates its scroll viewport and extent by items instead of pixels, so:

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

The viewport may show a different number of items at different times, meaning the number of items in the viewport as compared to the number of items in the extent varies, causing the scrollbar length to change, and

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

Scrolling moves from one item to the next and never in between, leading to "jerky" scrolling

滚动从一个项目移动到下一个项目,并且不会在两者之间移动,导致“生涩”滚动

but

As long as you're using VirtualizingStackPanel under the hood, it only needs to apply templates and measure and arrange the items that are actually visible at the moment, and

只要你在幕后使用 VirtualizingStackPanel,它只需要应用模板并测量和排列此刻实际可见的项目,并且

ScrollIntoView is much simpler since it just needs to get the right item index into view

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

Choosing between them

在他们之间做出选择

These are the only two kinds of scrolling provided by WPF. You must choose between them based on the above tradeoffs. Generally logical scrolling is best for medium to large datasets, and physical scrolling is best for small ones.

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

A trick to speed loading during physical scrolling is to make the physical scrolling better is to wrap your items in a custom Decorator that has a fixed size and sets its child's Visibility to Hidden when it is not visible. This prevents the ApplyTemplate, Measure and Arrange from occuring on the descendant controls of that item until you're ready for it to happen.

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

A trick to make physical scrolling's ScrollIntoView more reliable is to call it twice: Once immediately and once in a dispatcher callback of DispatcherPriority.ApplicationIdle.

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

Making logical scroll scrollbar more stable

使逻辑滚动条更稳定

If all your items are the same height, the number of items visible in the viewport at any time will stay the same, causing the scroll thumb size to stay the same (because the ratio with total number if items doesn't change).

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

It is also possible to modify the behavior of the ScrollBar itself so the thumb is always calculated to be a fixed size. To do this without any hacky code-behind:

还可以修改 ScrollBar 本身的行为,以便拇指始终计算为固定大小。要在没有任何hacky代码隐藏的情况下执行此操作:

  • Subclass Track to replace the calculation of Thumb position and size in MeasureOverride with your own
  • Change the ScrollBar template used
    for the logical-scrolling ScrollBar
    to use your subclassed Track instead of the regular one

  • Change the ScrollViewer template to explicitly set your custom ScrollBar template on the logical-scrolling ScrollBar
    (instead of using the default
    template)

  • Change the ListBox template to use explicitly set your custom
    ScrollViewer template on the
    ScrollViewer it creates
  • Subclass Track 用你自己的替换 MeasureOverride 中 Thumb 位置和大小的计算
  • 更改用于
    逻辑滚动 ScrollBar的 ScrollBar 模板
    以使用您的子类 Track 而不是常规的

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

  • 更改 ListBox 模板以使用

    它创建的ScrollViewer上显式设置您的自定义ScrollViewer 模板

This means copying a lot of template code fom the built-in WPF templates, so it is not a very elegant solution. But the alternative to this is to use hacky code-behind to wait until all the templates are expanded, then find the ScrollBar and just replace the ScrollBar template with the one that uses your custom Track. This code saves two large templates (ListBox, ScrollViewer) at the cost of some very tricky code.

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

Using a different Panel would be a much larger amount of work: VirtualizingStackPanel is the only Panel that virtualizes, and only it and StackPanel to logical scrolling. Since you are taking advantage of VirtualizingStackPanel's virtualization abilities you would have to re-implement all of these plus all IScrollInfo info function plus your regular Panel functions. I could do something like that but I would allocate several, perhaps many, days to get it right. I recommend you not try it.

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

Courtesy - Physical Scrolling vs Logical Scrolling

礼貌 -物理滚动与逻辑滚动

回答by Grant Thomas

They can differ from the point of (specified) Heightbeing evaluated to any given time during the (ongoing) rendering process.

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

From MSDN:

MSDN

There is a difference between the properties of Height and Width and ActualHeight and ActualWidth. For example, the ActualHeight property is a calculated value based on other height inputs and the layout system. The value is set by the layout system itself, based on an actual rendering pass, and may therefore lag slightly behind the set value of properties, such as Height, that are the basis of the input change.

Because ActualHeight is a calculated value, you should be aware that there could be multiple or incremental reported changes to it as a result of various operations by the layout system. The layout system may be calculating required measure space for child elements, constraints by the parent element, and so on.

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

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