ScrollViewer的视口高度 VS 实际高度

发布于 2024-11-02 19:56:05 字数 670 浏览 5 评论 0原文

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

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

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

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

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

为什么会有这样的差异?

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

有人可以解释一下两个列表框之间有什么区别吗?对于列表框 1,我需要 ViewPort 高度

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:

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.

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.

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.

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

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

猫性小仙女 2024-11-09 19:56:05

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

所以,总结一下:

ActualHeight = ViewportHeight + HorizontalScrollbarHeight

the ActualHeight is the actual height of the ScrollViewer. The Viewport is what is visible from the ScrollViewers Content. So to answer your question: ViewportHeight differs from ActualHeight if the horizontal Scrollbar is visible by the Height of the Scrollbar.

so, to sum this up:

ActualHeight = ViewportHeight + HorizontalScrollbarHeight
著墨染雨君画夕 2024-11-09 19:56:05

最后,这是根本原因:

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

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

正如您所发现的,每种方法都有其权衡。

物理滚动

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

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

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

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

视口可能在不同时间显示不同数量的项目,
表示视口中的项目数与数量相比
范围内的项目发生变化,导致滚动条长度发生变化,

滚动从一个项目移动到下一个项目,而不是在两者之间滚动,
导致“不稳定”滚动

但是

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

ScrollIntoView 更简单,因为它只需要获得正确的位置
视图中的项目索引

在它们之间进行选择

这是 WPF 提供的唯一两种滚动方式。你必须
根据上述权衡在它们之间进行选择。一般逻辑
滚动最适合中型到大型数据集,以及物理滚动
最适合小孩子。

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

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

让逻辑滚动条滚动条更加稳定

如果所有项目的高度相同,则可见的项目数
视口在任何时候都会保持不变,导致滚动滑块
大小保持不变(因为如果项目与总数的比率
不变)。

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

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

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

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

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

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

使用不同的面板会需要更多的工作量:
VirtualizingStackPanel 是唯一可以虚拟化的面板,而且也只有它
和 StackPanel 进行逻辑滚动。既然你正在利用
VirtualizingStackPanel 的虚拟化能力你必须得
重新实现所有这些加上所有 IScrollInfo 信息函数加上您的
常规面板功能。我可以做类似的事情,但我会
分配几天,也许很多天来把事情做好。我推荐你
不去尝试。

Finally This was the root cause:

Quoting from https://stackoverflow.com/a/3062692/3195477:

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:

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

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

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

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

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.

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.

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.

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:

  • 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

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.

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.

情绪少女 2024-11-09 19:56:05

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

来自 MSDN

两者之间是有区别的
高度和宽度的属性和
实际高度和实际宽度。为了
例如,ActualHeight 属性是
根据其他计算得出的值
高度输入和布局系统。
该值由布局系统设置
本身,基于实际渲染
通过,因此可能略有滞后
在属性的设定值后面,
例如身高,这是基础
输入变化。

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

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

From 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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文