9

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

Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
Rohit
  • 6,365
  • 14
  • 59
  • 90

3 Answers3

12

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
Dave Clemmer
  • 3,741
  • 12
  • 49
  • 72
Markus Hütter
  • 7,796
  • 1
  • 36
  • 63
  • 1
    Can you please explain the relation between horizontal scrollbar visibility with Viewport Height ???? – Rohit Apr 21 '11 at 06:37
  • 1
    @Rohit didn't I already? Here a specific example: The ActualHeight is 200px. The Horizontal Scrollbar has a height of 12px. If the Horizontal Scrollbar _is_ shown, the Viewport Height is 188px, if it _is not_ shown the Viewport height is 200px. And whether the Scrollbar is shown depends on the Property `HorizontalScrollBarVisibility`. – Markus Hütter Apr 21 '11 at 10:11
  • Thanks for reply. I understand this thing (said above) but I'm curious to know how these things are related to each other. As much i understand Viewport Height should work considering ActualHeight & Vertical Scrollbar visibility. – Rohit Apr 21 '11 at 10:22
  • @Rohit I don't really know what more to explain. The Viewport's size is that of the ScrollViewer minus the area of the Scrollbars. you can only __get__ the Viewport's dimensions (not set it). It gets calculated depending on the ActualHeight (whicht itsself is readonly and also gets calculated (by the `Height` property and restraints on the control)) and the scrollbars. It exists so you can bind to it and don't have to use a valueconverter to calculate `ActualHeight - (IsHorizontalScrollBarVisible ? HorizontalScrollbarHeight : 0)`. – Markus Hütter Apr 21 '11 at 10:46
  • This asnwer is only correct for the cases when `CanContentScroll` is `false` The viewport height of a `ScrollViewer` in a `ListView` for example will always be the number of items the `ScrollViewer`can display. – bitbonk Nov 25 '16 at 12:22
  • @bitbonk this answer addressed the question in its original form. It wasn't apparent until after OP edited it, that he was actually talking about a `ListView` – Markus Hütter Aug 11 '17 at 13:22
9

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.

StayOnTarget
  • 11,743
  • 10
  • 52
  • 81
Rohit
  • 6,365
  • 14
  • 59
  • 90
  • 2
    next time you're posting a question post all relevant information right from the start. You could have for example stated that you are talking about a ScrollViewer inside an ItemsControl which is a totally different scenario than the usual ScrollViewer that just lets you scroll through some content. – Markus Hütter Apr 25 '11 at 12:04
1

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.

Grant Thomas
  • 44,454
  • 10
  • 85
  • 129