使用ScrollViewer单击并拖动滚动

本文关键字:拖动 滚动 单击 ScrollViewer 使用 | 更新日期: 2023-09-27 17:58:19

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

我有一个嵌套在ScrollViewer中的StackPanel,并且我已经可以滚动了。我相信我在某个地方看到了答案,但我似乎再也找不到了。

这只能使用代码来完成。

使用ScrollViewer单击并拖动滚动

看看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事件的处理程序中,而Loaded事件又永远不会解除连接。

这意味着里面有某种"内存泄漏",但这是我准备接受的。