Wpf ScrollViewer Scroll Amount

Viewed 17820

Is it possible to change the amount that the WPF ScrollViewer scrolls? I am simply wondering if it's possible to change the scrollviewer so that when using the mouse wheel or the scrollviewer arrows, the amount of incremental scrolling can be changed.

5 Answers

Here's a simple, complete and working WPF ScrollViewer class that has a data-bindable SpeedFactor property for adjusting the mouse wheel sensitivity. Setting SpeedFactor to 1.0 means identical behavior to the WPF ScrollViewer. The default value for the dependency property is 2.5, which allows for very speedy wheel scrolling.

Of course, you can also create additional useful features by binding to the SpeedFactor property itself, i.e., to easily allow the user to control the multiplier.

public class WheelSpeedScrollViewer : ScrollViewer
{
    public static readonly DependencyProperty SpeedFactorProperty =
        DependencyProperty.Register(nameof(SpeedFactor),
                                    typeof(Double),
                                    typeof(WheelSpeedScrollViewer),
                                    new PropertyMetadata(2.5));

    public Double SpeedFactor
    {
        get { return (Double)GetValue(SpeedFactorProperty); }
        set { SetValue(SpeedFactorProperty, value); }
    }

    protected override void OnPreviewMouseWheel(MouseWheelEventArgs e)
    {
        if (!e.Handled && 
            ScrollInfo is ScrollContentPresenter scp &&
            ComputedVerticalScrollBarVisibility == Visibility.Visible)
        {
            scp.SetVerticalOffset(VerticalOffset - e.Delta * SpeedFactor);
            e.Handled = true;
        }
    }
};

Complete XAML demo of 'fast mouse wheel scrolling' of around 3200 data items:

note: 'mscorlib' reference is only for accessing the demonstration data.

<UserControl x:Class="RemoveDuplicateTextLines.FastScrollDemo"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:MyApp"
    xmlns:sys="clr-namespace:System;assembly=mscorlib">

    <local:WheelSpeedScrollViewer VerticalScrollBarVisibility="Auto">
        <ListBox ItemsSource="{Binding Source={x:Type sys:Object},Path=Assembly.DefinedTypes}" />
    </local:WheelSpeedScrollViewer>

</UserControl>

Fast mouse wheel:

enter image description here

I wanted to add to Drew Marsh accepted answer - while the other suggested answers solve it, in some cases you cannot override the PreviewMouseWheel event and handle it without causing other side effects. Namely if you have child controls that should receive priority to be scrolled before the parent ScrollViewer - like nested ListBox or ComboBox popups.

In my scenario, my parent control was a ItemsControl with its ItemsPanel being a VirtualizingStackPanel. I wanted its logical scrolling to be 1 unit per item instead of the default 3. Instead of fiddling with attached behaviors and intercepting/handling the mouse wheel events, I simply implemented a custom VirtualizingStackPanel to do this.

    public class VirtualizingScrollSingleItemAtATimeStackPanel : VirtualizingStackPanel
    {
        public override void MouseWheelDown()
        {
            PageDown();
        }

        public override void MouseWheelUp()
        {
            PageUp();
        }

        public override void PageDown()
        {
            LineDown();
        }

        public override void PageUp()
        {
            LineUp();
        }
    }

then we use that panel like we normally would in our xaml markup:

<ItemsControl>
    <ItemsControl.ItemsPanel>
        <ItemsPanelTemplate>
            <controls:VirtualizingScrollSingleItemAtATimeStackPanel />
        </ItemsPanelTemplate>
    </ItemsControl.ItemsPanel>
</ItemsControl>

Obviously my scenario is contrived and the solution very simple, however this might provide a path for others to have better control over the scrolling behavior without the side effects I encountered.

Related