如何使WPF ScrollViewer中键单击滚动?

时间:2011-03-24 22:14:14

标签: wpf scroll scrollviewer mousewheel

单击鼠标中键(又名:鼠标滚轮),然后稍微向下移动鼠标,用户可以在IE和大多数Windows应用程序中滚动。默认情况下,WPF控件中似乎缺少此行为?是否有设置,变通方法或明显缺少的东西?

2 个答案:

答案 0 :(得分:12)

我已经找到了如何使用3个鼠标事件(MouseDownMouseUpMouseMove)来实现此目的。他们的处理程序附加到下面的xaml中的ScrollViewer元素:

<Grid>
    <ScrollViewer MouseDown="ScrollViewer_MouseDown" MouseUp="ScrollViewer_MouseUp" MouseMove="ScrollViewer_MouseMove">
            <StackPanel x:Name="dynamicLongStackPanel">

            </StackPanel>
    </ScrollViewer>
    <Canvas x:Name="topLayer" IsHitTestVisible="False" />
</Grid>

最好在代码隐藏中编写行为而不是事件,但不是每个人都有必要的库,而且我也不知道如何将它与Canvas连接起来。

事件处理程序:

    private bool isMoving = false;                  //False - ignore mouse movements and don't scroll
    private bool isDeferredMovingStarted = false;   //True - Mouse down -> Mouse up without moving -> Move; False - Mouse down -> Move
    private Point? startPosition = null;
    private double slowdown = 200;                  //The number 200 is found from experiments, it should be corrected



    private void ScrollViewer_MouseDown(object sender, MouseButtonEventArgs e)
    {
        if (this.isMoving == true) //Moving with a released wheel and pressing a button
                this.CancelScrolling();
        else if (e.ChangedButton == MouseButton.Middle && e.ButtonState == MouseButtonState.Pressed)
        {
            if (this.isMoving == false) //Pressing a wheel the first time
            {
                this.isMoving = true;
                this.startPosition = e.GetPosition(sender as IInputElement);
                this.isDeferredMovingStarted = true; //the default value is true until the opposite value is set

                this.AddScrollSign(e.GetPosition(this.topLayer).X, e.GetPosition(this.topLayer).Y);
            }
        }
    }

    private void ScrollViewer_MouseUp(object sender, MouseButtonEventArgs e)
    {
        if (e.ChangedButton == MouseButton.Middle && e.ButtonState == MouseButtonState.Released && this.isDeferredMovingStarted != true)
            this.CancelScrolling();
    }

    private void CancelScrolling()
    {
        this.isMoving = false;
        this.startPosition = null;
        this.isDeferredMovingStarted = false;
        this.RemoveScrollSign();
    }

    private void ScrollViewer_MouseMove(object sender, MouseEventArgs e)
    {
        var sv = sender as ScrollViewer;

        if (this.isMoving && sv != null)
        {
            this.isDeferredMovingStarted = false; //standard scrolling (Mouse down -> Move)

            var currentPosition = e.GetPosition(sv);
            var offset = currentPosition - startPosition.Value;
            offset.Y /= slowdown;
            offset.X /= slowdown;

            //if(Math.Abs(offset.Y) > 25.0/slowdown)  //Some kind of a dead space, uncomment if it is neccessary
            sv.ScrollToVerticalOffset(sv.VerticalOffset + offset.Y);
            sv.ScrollToHorizontalOffset(sv.HorizontalOffset + offset.X);
        }
    }

如果要移除方法调用AddScrollSignRemoveScrollSign,此示例将起作用。但是我用2种设置滚动图标的方法扩展了它:

    private void AddScrollSign(double x, double y)
    {
        int size = 50;
        var img = new BitmapImage(new Uri(@"d:\middle_button_scroll.png"));
        var adorner = new Image() { Source = img, Width = size, Height = size };
        //var adorner = new Ellipse { Stroke = Brushes.Red, StrokeThickness = 2.0, Width = 20, Height = 20 };

        this.topLayer.Children.Add(adorner);
        Canvas.SetLeft(adorner, x - size / 2);
        Canvas.SetTop(adorner, y - size / 2);
    }

    private void RemoveScrollSign()
    {
        this.topLayer.Children.Clear();
    }

图标示例: enter image description here enter image description here

最后一句话:Press -> Immediately Release -> Move的方式存在一些问题。如果用户单击鼠标左键或任何键盘键或应用程序失去焦点,则应取消滚动。有很多事件,我没有时间处理它们。

但标准方式Press -> Move -> Release可以毫无问题地工作。

答案 1 :(得分:2)

vorrtex 发布了一个不错的解决方案,请向他投票

我确实对他的解决方案有一些建议,这些建议过于冗长,无法将它们全部纳入评论中,这就是我发布单独答案并将其指向他的原因!

你提到Press-&gt; Release-&gt; Move的问题。即使鼠标不在ScrollViewer之上,您也应该使用MouseCapturing来获取MouseEvents。我没有对它进行过测试,但我猜你的解决方案在Press->Move->Move outside of ScrollViewer->Release中也失败了,Mousecapturing也会对此进行处理。

您还提到使用行为。我宁愿建议一个不需要额外依赖的attached behavior

您绝对不应该使用额外的Canvas,而是在Adorner中执行此操作。

ScrollViewer自身托管了一个定义AdornerLayer的ScrollContentPresenter。你应该在那里插入Adorner。这消除了对任何进一步依赖性的需要,并且还使附加行为像IsMiddleScrollable="true"一样简单。