WPF Standard Control Demo App › ScrollViewer

ScrollViewer Layout

ScrollViewer shows part of content that is larger than itself and lets the user scroll through the rest. ListBox, TextBox, TreeView, and DataGrid each contain one.

Four scroll bar settings, with content that fits or overflows

VerticalScrollBarVisibility and HorizontalScrollBarVisibility decide whether the bar is shown and whether the content can scroll. Like the demo app, a ScrollViewer 200 wide and 100 high was given 2 labels, which fit, and 9, which do not:

ComputedVerticalScrollBarVisibility, which tells whether the bar is actually shown, was Visible only for Auto with 9 labels and for Visible; in every other case it was Collapsed. These values were measured for the vertical bar. HorizontalScrollBarVisibility accepts the same four values; the horizontal direction was not measured.

VerticalScrollBarVisibility items Computed... ViewportWidth ExtentHeight offset after scrolling to 50
Disabled 2 Collapsed 200 55.92 0
Disabled 9 Collapsed 200 100 0
Auto 2 Collapsed 200 55.92 0
Auto 9 Visible 183 251.64 50
Hidden 2 Collapsed 200 55.92 0
Hidden 9 Collapsed 200 251.64 50
Visible 2 Visible 183 55.92 0
Visible 9 Visible 183 251.64 50

Each VerticalScrollBarVisibility value with content that fits and content that overflows. Measured on .NET 10 / Windows 11.

Defaults, and changing the ScrollViewer inside a control

By default a ScrollViewer has VerticalScrollBarVisibility Visible and HorizontalScrollBarVisibility Disabled. The controls that contain one set their own values. The ScrollViewer inside a ListBox, a TreeView, and a DataGrid had Auto horizontally, and the one inside a TextBox had Hidden.

To change the ScrollViewer inside a control, set the attached property on the control itself. On a ListBox, ScrollViewer.HorizontalScrollBarVisibility="Disabled" reached the ScrollViewer inside it. Putting the ListBox in another ScrollViewer set to Disabled did not: the inner one stayed Auto.

CanContentScroll: pixels or items, and virtualization

CanContentScroll decides whether the content scrolls by its own units. With the demo app’s StackPanel of 9 labels, False gave ExtentHeight 251.64 and ViewportHeight 100 in pixels, and one LineDown moved 16. True gave 9 and 3, counted in items, and one LineDown moved one item.

In a list it also affects virtualization. A ListBox with default settings, 1000 items, and 200 high created 10 ListBoxItems with True and all 1000 with False. False turns virtualization off, but True alone does not turn it on: the items must also be in a VirtualizingStackPanel with VirtualizingPanel.IsVirtualizing left True, as they are by default in a ListBox. Keep it True in long lists.

Deferred scrolling and when the sizes are known

IsDeferredScrollingEnabled makes the content wait until the thumb is released. With False, dragging the thumb 30 down moved both VerticalOffset and ContentVerticalOffset to 114.38 during the drag. With True, both stayed at 0 during the drag and moved to 114.38 on release. The drag was reproduced by raising the thumb’s drag events (started, a delta of 30, completed), not with a real mouse.

The size properties are known only after layout. ExtentHeight and ViewportHeight were 0 before the first layout and 251.64 and 100 after it. Why a ScrollViewer sometimes does not scroll at all, for example inside a StackPanel, is covered in the article linked below.

case measured
defaults: VerticalScrollBarVisibility / HorizontalScrollBarVisibility Visible / Disabled
demo items, CanContentScroll=False: sizes / offset after LineDown extent 251.64, viewport 100 / 16
demo items, CanContentScroll=True: sizes / offset after LineDown extent 9, viewport 3 / 1
ListBox, 1000 items, CanContentScroll=True: items created 10
ListBox, 1000 items, CanContentScroll=False: items created 1000
IsDeferredScrollingEnabled=False: offset / content offset, dragging; released 114.38 / 114.38; 114.38 / 114.38
IsDeferredScrollingEnabled=True: offset / content offset, dragging; released 0 / 0; 114.38 / 114.38
HorizontalScrollBarVisibility inside ListBox / TextBox / TreeView / DataGrid Auto / Hidden / Auto / Auto
inside ListBox: attached Disabled / outer ScrollViewer Disabled Disabled / Auto
ExtentHeight / ViewportHeight before layout; after layout 0 / 0; 251.64 / 100

CanContentScroll, deferred scrolling, and the ScrollViewers inside controls. Measured on .NET 10 / Windows 11.

Trying it in the demo app

The ScrollViewer page of the demo app, with the control list on the left and the first section, CanContentScroll

The ScrollViewer page of the demo app has sections for CanContentScroll, IsDeferredScrollingEnabled, both scroll bar settings, and the read-only size and offset properties in each direction; it shows those values for 2 and 9 labels, and for labels placed side by side. Each section uses a ScrollViewer 100 high holding labels with a border. The “Show Code” link under each section displays its XAML. The following XAML is the CanContentScroll section (ScrollViewerUsageControl.xaml), with the styles and the surrounding GroupBoxes left out and the namespace declarations added. The styles set the ScrollViewer’s Height to 100 and give each label a black border 1 wide.

<StackPanel xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
            xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
  <CheckBox x:Name="CanContentScrollCheckBox" Content="CanContentScroll" />

  <ScrollViewer x:Name="CanContentScrollScrollViewer"
                CanContentScroll="{Binding IsChecked, ElementName=CanContentScrollCheckBox}"
                Height="100">
    <StackPanel Orientation="Vertical">
      <Label Content="Item1" />
      <Label Content="Item2" />
      <Label Content="Item3" />
      <Label Content="Item4" />
      <Label Content="Item5" />
      <Label Content="Item6" />
      <Label Content="Item7" />
      <Label Content="Item8" />
      <Label Content="Item9" />
    </StackPanel>
  </ScrollViewer>
</StackPanel>

Source code and how it was measured

Every behavior on this page was measured by running it on .NET 10 / Windows 11, using ScrollViewerDemoScene in this site’s screenshot tool. The content was the demo app’s labels with a border. Dragging the thumb was reproduced by raising the same DragStarted, DragDelta, and DragCompleted events on it that a mouse drag raises. Sizes are the values on the measuring machine.

View ScrollViewer source code on GitHub →