Wpf ScrollViewer滚动金额

时间:2009-10-28 19:21:21

标签: wpf scrollviewer

是否可以更改WPF ScrollViewer滚动的数量?我只是想知道是否可以更改滚动查看器,以便在使用鼠标滚轮或滚动查看器箭头时,可以更改增量滚动的数量。

5 个答案:

答案 0 :(得分:14)

简短的回答是:没有编写一些自定义滚动代码就没有办法做到这一点,但不要让你吓唬它并不是那么难。

ScrollViewer通过使用物理单位(即像素)滚动或通过与IScrollInfo实现交互来使用逻辑单元。这由设置the CanContentScroll property控制,其中值false表示“使用物理单位滚动内容”,值true表示“逻辑滚动内容”。

那么ScrollViewer如何在逻辑上滚动内容?通过与IScrollInfo实现进行通信。因此,当有人执行逻辑操作时,您可以准确地接管面板内容滚动的程度。 Take a look at the documentation for IScrollInfo获取可以请求滚动的所有测量逻辑单元的列表,但是由于您提到了鼠标滚轮,因此您最感兴趣的是MouseWheelUp / Down / Left / Right方法。

答案 1 :(得分:2)

这是一个简单,完整且有效的 WPF ScrollViewer类,具有可绑定数据的SpeedFactor属性,用于调整鼠标滚轮灵敏度。将SpeedFactor设置为1.0意味着与WPF ScrollViewer相同的行为。依赖项属性的默认值为 2.5 ,这允许快速滚动滚轮。

当然,您还可以通过绑定到SpeedFactor属性本身来创建其他有用的功能,即轻松允许用户控制乘数。

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;
        }
    }
};

完成大约3200个数据项的“快速鼠标滚轮滚动” XAML演示:

<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>

快速鼠标滚轮:

enter image description here

答案 2 :(得分:1)

您可以在scrollviewer上实现一种行为。就我而言,CanContentScroll没有用。下面的解决方案适用于使用鼠标滚轮滚动以及拖动滚动条。

public class StepSizeBehavior : Behavior<ScrollViewer>
{
    public int StepSize { get; set; }

    #region Attach & Detach
    protected override void OnAttached()
    {
        CheckHeightModulesStepSize();
        AssociatedObject.ScrollChanged += AssociatedObject_ScrollChanged;
        base.OnAttached();
    }

    protected override void OnDetaching()
    {
        AssociatedObject.ScrollChanged -= AssociatedObject_ScrollChanged;
        base.OnDetaching();
    }
    #endregion

    [Conditional("DEBUG")]
    private void CheckHeightModulesStepSize()
    {
        var height = AssociatedObject.Height;
        var remainder = height%StepSize;
        if (remainder > 0)
        {
            throw new ArgumentException($"{nameof(StepSize)} should be set to a value by which the height van be divised without a remainder.");
        }
    }

    private void AssociatedObject_ScrollChanged(object sender, ScrollChangedEventArgs e)
    {
        const double stepSize = 62;
        var scrollViewer = (ScrollViewer)sender;
        var steps = Math.Round(scrollViewer.VerticalOffset / stepSize, 0);
        var scrollPosition = steps * stepSize;
        if (scrollPosition >= scrollViewer.ScrollableHeight)
        {
            scrollViewer.ScrollToBottom();
            return;
        }
        scrollViewer.ScrollToVerticalOffset(scrollPosition);
    }
}

您可以这样使用它:

<ScrollViewer MaxHeight="248"
              VerticalScrollBarVisibility="Auto">
    <i:Interaction.Behaviors>
        <behaviors:StepSizeBehavior StepSize="62" />
    </i:Interaction.Behaviors>

答案 3 :(得分:0)

我这样做是为了确保scrollbar1.ValueChanged上的整数:

final AlertDialog.Builder builder = new AlertDialog.Builder(Friends.this);
LayoutInflater inflater = Friends.this.getLayoutInflater();
                final View dialogView = inflater.inflate(R.layout.custom_builder, null);
close.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {

                    }
                });
 builder.show();

答案 4 :(得分:0)

我想添加 Drew Marsh 接受的答案 - 虽然其他建议的答案解决了这个问题,但在某些情况下,您无法覆盖 PreviewMouseWheel 事件并在不引起其他副作用的情况下处理它。也就是说,如果您的子控件应该优先在父控件 ScrollViewer 之前滚动 - 例如嵌套的 ListBoxComboBox 弹出窗口。

在我的场景中,我的父控件是一个 ItemsControl,它的 ItemsPanel 是一个 VirtualizingStackPanel。我希望它的逻辑滚动为每个项目 1 个单位,而不是默认的 3 个单位。我没有摆弄附加行为和拦截/处理鼠标滚轮事件,而是简单地实现了一个自定义 VirtualizingStackPanel 来做到这一点。

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

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

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

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

然后我们像往常一样在 xaml 标记中使用该面板:

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

显然,我的场景是人为设计的,解决方案非常简单,但是这可能会为其他人提供一条途径,让他们更好地控制滚动行为,而不会遇到我遇到的副作用。