Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SmallChange in a XAML ScrollViewer ControlTemplate has no effect

Tags:

wpf

xaml

I've created a basic Windows Desktop WPF Application. In the MainWindow, I've added the following as the body of the window:

    <ScrollViewer Template="{DynamicResource ScrollViewerControlTemplate1}">
        <ScrollViewer.Resources>
            <ControlTemplate x:Key="ScrollViewerControlTemplate1" TargetType="{x:Type ScrollViewer}">
                <Grid x:Name="Grid" Background="{TemplateBinding Background}">
                    <Grid.ColumnDefinitions>
                        <ColumnDefinition Width="*"/>
                        <ColumnDefinition Width="Auto"/>
                    </Grid.ColumnDefinitions>
                    <Grid.RowDefinitions>
                        <RowDefinition Height="*"/>
                        <RowDefinition Height="Auto"/>
                    </Grid.RowDefinitions>
                    <Rectangle x:Name="Corner" Grid.Column="1" Fill="{DynamicResource {x:Static SystemColors.ControlBrushKey}}" Grid.Row="1"/>
                    <ScrollContentPresenter x:Name="PART_ScrollContentPresenter" CanContentScroll="{TemplateBinding CanContentScroll}" CanHorizontallyScroll="False" CanVerticallyScroll="False" ContentTemplate="{TemplateBinding ContentTemplate}" Content="{TemplateBinding Content}" Grid.Column="0" Margin="{TemplateBinding Padding}" Grid.Row="0"/>
                    <ScrollBar x:Name="PART_VerticalScrollBar" AutomationProperties.AutomationId="VerticalScrollBar" Cursor="Arrow" Grid.Column="1" Maximum="{TemplateBinding ScrollableHeight}" Minimum="0" Grid.Row="0" Visibility="{TemplateBinding ComputedVerticalScrollBarVisibility}" Value="{Binding VerticalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportHeight}" SmallChange="40000"/>
                    <ScrollBar x:Name="PART_HorizontalScrollBar" AutomationProperties.AutomationId="HorizontalScrollBar" Cursor="Arrow" Grid.Column="0" Maximum="{TemplateBinding ScrollableWidth}" Minimum="0" Orientation="Horizontal" Grid.Row="1" Visibility="{TemplateBinding ComputedHorizontalScrollBarVisibility}" Value="{Binding HorizontalOffset, Mode=OneWay, RelativeSource={RelativeSource TemplatedParent}}" ViewportSize="{TemplateBinding ViewportWidth}"/>
                </Grid>
            </ControlTemplate>
        </ScrollViewer.Resources>
        <Grid Margin="20">
            <Grid.RowDefinitions>
                <RowDefinition Height="300"/>
                <RowDefinition Height="300"/>
                <RowDefinition Height="300"/>
                <RowDefinition Height="300"/>
                <RowDefinition Height="300"/>
            </Grid.RowDefinitions>

            <Border Grid.Row="0" Background="SteelBlue"/>
            <Border Grid.Row="1" Background="Peru"/>
            <Border Grid.Row="2" Background="Goldenrod"/>
            <Border Grid.Row="3" Background="Tomato"/>
            <Border Grid.Row="4" Background="IndianRed"/>
        </Grid>

    </ScrollViewer>

You'll notice that on PART_VerticalScrollbar, I've set the SmallChange="40000" (an arbitrarily large number). Yet when I click the up/down arrows on the scrollbar, it does the same very small change as it did before I set the SmallChange to anything.

I've read over the documentation a number of times, and can't figure out why this isn't having any effect on the amount that the ScrollViewer scrolls. Any ideas?

Note that I could change the ScrollBar template and change the command these button calls to Scrollbar.PageUpCommand rather than Scrollbar.LineUpCommand, but ultimately I'd like to have finer control over the scrolling than a full page.

like image 265
Mike Cullingham Avatar asked Jun 02 '15 15:06

Mike Cullingham


1 Answers

The reason for this is a special logic implemented in the ScrollViewer and in the ScrollBar.

The ScrollBar.SmallChange property will be only considered for scrolling if the scroll bar is stand-alone. That means, when it is outside of a ScrollViewer.

If you look at the ScrollViewer.OnApplyTemplate method, you will notice the following:

public override void OnApplyTemplate()
{
    // ...
    ScrollBar scrollBar = GetTemplateChild(HorizontalScrollBarTemplateName) as ScrollBar;

    if (scrollBar != null)
        scrollBar.IsStandalone = false;

    // Same for the vertical scroll bar
    // ...
}

If a ScrollViewer finds scroll bars inside of it, it sets their IsStandalone (internal) properties to false, which disables the scroll command handing of the scroll bars. Instead, the ScrollViewer takes over the scroll command handing, but it ignores some ScrollBar's properties, including the SmallChange property.

TL;DR This won't work for the scroll bars inside of a ScrollViewer. You can change your template so use two "external" scroll bars. But then, you will need to connect those scroll bars with the ScrollViewer manually.

like image 99
dymanoid Avatar answered Oct 13 '22 04:10

dymanoid