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:
Disabledturns scrolling off. The content was given only the viewer’s height (ExtentHeight100 for 9 labels), so the rest is cut off, and scrolling to 50 left the offset at 0.Autoshowed the bar only for 9 labels.Hiddennever showed it, but scrolling to 50 worked. UseHidden, notDisabled, to hide the bar but keep scrolling.Visibleshowed it even for 2 labels. The bar takes width:ViewportWidthwas 183 with the bar and 200 without, so preferAutowhen there may be nothing to scroll.
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 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>
Related controls and articles
- ListBox — contains a ScrollViewer that the attached properties change.
- StackPanel — a common child of a ScrollViewer, which scrolls by items when
CanContentScrollisTrue. - Viewbox — scales content to fit instead of scrolling it.
- Why a WPF ScrollViewer Does Not Scroll and How to Fix It — the layouts in which a ScrollViewer gets no scroll bar.
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.