ScrollViewer 的视口高度 VS 实际高度 [英] ScrollViewer's Viewport Height VS Actual Height

查看:33
本文介绍了ScrollViewer 的视口高度 VS 实际高度的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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?

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

One more question: I read on MSDN:

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

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

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.

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

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.

为什么会有这种差异?

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

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

推荐答案

最后这是根本原因:

引用自 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.

物理滚动

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

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

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

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

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

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

但是

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

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 要简单得多,因为它只需要获得正确的项目索引进入视图

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

在它们之间进行选择

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

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.

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

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.

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

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.

使逻辑滚动条更稳定

如果所有项目的高度相同,则显示的项目数视口在任何时候都将保持不变,从而导致滚动拇指大小保持不变(因为如果项目与总数的比率不变).

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

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

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:

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

  • Subclass Track to replace the calculation of Thumb position and size in MeasureOverride with your own

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

Change the ScrollBar template used for the logical-scrolling ScrollBar to use your subclassed Track instead of the regular one

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

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

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

Change the ListBox template to use explicitly set your custom ScrollViewer template on the ScrollViewer it creates

这意味着从内置 WPF 中复制大量模板代码模板,所以它不是一个非常优雅的解决方案.但另一种选择这是使用hacky code-behind等到所有模板展开,然后找到 ScrollBar 并替换 ScrollBar使用您的自定义轨道的模板.这段代码节省了两个大型模板(ListBox、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.

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

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.

这篇关于ScrollViewer 的视口高度 VS 实际高度的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆