使用ScrollViewer单击并拖动滚动

时间:2013-02-08 20:43:11

标签: c# wpf scrollviewer

我想允许使用ScrollViewer点击并拖动滚动(即点击ScrollViewer中的任意位置然后向上或向下拖动,它会相应滚动)

我有一个StackPanel嵌套在ScrollViewer内,我已经滚动了。我相信我在某个地方看到了答案,但我似乎无法再找到它了。

这必须仅使用代码完成。

1 个答案:

答案 0 :(得分:5)

查看this code from Matt Hamilton

public class TouchScrolling : DependencyObject
{
    public static bool GetIsEnabled(DependencyObject obj)
    {
        return (bool)obj.GetValue(IsEnabledProperty);
    }

    public static void SetIsEnabled(DependencyObject obj, bool value)
    {
        obj.SetValue(IsEnabledProperty, value);
    }

    public bool IsEnabled
    {
        get { return (bool)GetValue(IsEnabledProperty); }
        set { SetValue(IsEnabledProperty, value); }
    }

    public static readonly DependencyProperty IsEnabledProperty =
        DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(TouchScrolling), new UIPropertyMetadata(false, IsEnabledChanged));

    static Dictionary<object, MouseCapture> _captures = new Dictionary<object, MouseCapture>();

    static void IsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
    {
        var target = d as ScrollViewer;
        if (target == null) return;

        if ((bool)e.NewValue)
        {
            target.Loaded += target_Loaded;
        }
        else
        {
            target_Unloaded(target, new RoutedEventArgs());
        }
    }

    static void target_Unloaded(object sender, RoutedEventArgs e)
    {
        System.Diagnostics.Debug.WriteLine("Target Unloaded");

        var target = sender as ScrollViewer;
        if (target == null) return;

        _captures.Remove(sender);

        target.Loaded -= target_Loaded;
        target.Unloaded -= target_Unloaded;
        target.PreviewMouseLeftButtonDown -= target_PreviewMouseLeftButtonDown;
        target.PreviewMouseMove -= target_PreviewMouseMove;

        target.PreviewMouseLeftButtonUp -= target_PreviewMouseLeftButtonUp;
    }

    static void target_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
    {
        var target = sender as ScrollViewer;
        if (target == null) return;

        _captures[sender] = new MouseCapture
        {
            VerticalOffset = target.VerticalOffset,
            Point = e.GetPosition(target),
        };
    }

    static void target_Loaded(object sender, RoutedEventArgs e)
    {
        var target = sender as ScrollViewer;
        if (target == null) return;

        System.Diagnostics.Debug.WriteLine("Target Loaded");

        target.Unloaded += target_Unloaded;
        target.PreviewMouseLeftButtonDown += target_PreviewMouseLeftButtonDown;
        target.PreviewMouseMove += target_PreviewMouseMove;

        target.PreviewMouseLeftButtonUp += target_PreviewMouseLeftButtonUp;
    }

    static void target_PreviewMouseLeftButtonUp(object sender, MouseButtonEventArgs e)
    {
        var target = sender as ScrollViewer;
        if (target == null) return;

        target.ReleaseMouseCapture();
    }

    static void target_PreviewMouseMove(object sender, MouseEventArgs e)
    {
        if (!_captures.ContainsKey(sender)) return;

        if (e.LeftButton != MouseButtonState.Pressed)
        {
            _captures.Remove(sender);
            return;
        }

        var target = sender as ScrollViewer;
        if (target == null) return;

        var capture = _captures[sender];

        var point = e.GetPosition(target);

        var dy = point.Y - capture.Point.Y;
        if (Math.Abs(dy) > 5)
        {
            target.CaptureMouse();
        }

        target.ScrollToVerticalOffset(capture.VerticalOffset - dy);
    }

    internal class MouseCapture
    {
        public Double VerticalOffset { get; set; }
        public Point Point { get; set; }
    }

}

这里有一些怪癖。我注意到ScrollViewer实际上是在显示内容时被加载,卸载和再次加载。

这意味着我无法在IsEnabledChanged方法中挂接事件并在target_Unloaded事件处理程序中取消挂钩,因为它们是立即解除挂钩的。相反,我不得不在Loaded事件的处理程序中将它们连接起来,而事件又永远不会被取消。

这意味着那里有一些“内存泄漏”,但这是我准备好接受的。