使用 ScrollViewer 单击并拖动滚动 [英] Click and drag scrolling using ScrollViewer

查看:57
本文介绍了使用 ScrollViewer 单击并拖动滚动的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

我有一个 StackPanel 嵌套在 ScrollViewer 中,并且我已经可以进行滚动了.我相信我在某处看到过那个答案,但我似乎再也找不到了.

这只能使用代码来完成.

解决方案

看看 这段来自 Matt Hamilton 的代码:

public class TouchScrolling : DependencyObject{public static bool GetIsEnabled(DependencyObject obj){返回(布尔)obj.GetValue(IsEnabledProperty);}public static void SetIsEnabled(DependencyObject obj, bool value){obj.SetValue(IsEnabledProperty, value);}公共布尔值已启用{获取{返回(布尔)GetValue(IsEnabledProperty);}set { SetValue(IsEnabledProperty, value);}}public static readonly DependencyProperty IsEnabledProperty =DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(TouchScrolling), new UIPropertyMetadata(false, IsEnabledChanged));静态字典<对象,MouseCapture>_captures = new Dictionary();静态无效 IsEnabledChanged(DependencyObject d,DependencyPropertyChangedEventArgs e){var target = d as ScrollViewer;if (target == null) 返回;if ((bool)e.NewValue){target.Loaded += target_Loaded;}别的{target_Unloaded(target, new RoutedEventArgs());}}静态无效目标_Unloaded(对象发送者,RoutedEventArgs e){System.Diagnostics.Debug.WriteLine("目标卸载");var target = sender as ScrollViewer;if (target == null) 返回;_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) 返回;_captures[sender] = new MouseCapture{VerticalOffset = target.VerticalOffset,Point = e.GetPosition(target),};}静态无效目标_加载(对象发送者,RoutedEventArgs e){var target = sender as ScrollViewer;if (target == null) 返回;System.Diagnostics.Debug.WriteLine("目标加载");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) 返回;target.ReleaseMouseCapture();}static void target_PreviewMouseMove(object sender, MouseEventArgs e){如果 (!_captures.ContainsKey(sender)) 返回;if (e.LeftButton != MouseButtonState.Pressed){_captures.Remove(sender);返回;}var target = sender as ScrollViewer;if (target == null) 返回;var 捕获 = _captures[sender];var point = e.GetPosition(target);var dy = point.Y - capture.Point.Y;如果 (Math.Abs​​(dy) > 5){target.CaptureMouse();}target.ScrollToVerticalOffset(capture.VerticalOffset - dy);}内部类 MouseCapture{公共双垂直偏移{得到;放;}公共点点{得到;放;}}

}

这里有一些怪癖.我注意到在显示内容时实际上正在加载、卸载和再次加载 ScrollViewer.

这意味着我不能只在 IsEnabledChanged 方法中连接事件并在 target_Unloaded 事件处理程序中取消它们,因为它们会立即被取消.相反,我不得不将它们挂接到 Loaded 事件的处理程序中,而后者永远不会解开.

这意味着存在某种内存泄漏",但我已准备好接受它.

I would like to allow click and drag scrolling using a ScrollViewer (i.e. click anywhere in the ScrollViewer and drag up or down, and it will scroll accordingly)

I have a StackPanel nested inside a ScrollViewer and I already have the scrolling working. I believe I saw that answer somewhere, but I can't seem to find it anymore.

This has to be done using code only.

解决方案

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

}

There are some quirks here. I noticed that the ScrollViewer was actually being loaded, unloaded and loaded again when the content was shown.

That meant that I couldn't just hook up the events in the IsEnabledChanged method and unhook them in the target_Unloaded event handler, because they were being unhooked immediately. Instead, I've had to hook them up in a handler for the Loaded event, which in turn never gets unhooked.

That means that there's something of a "memory leak" in there, but it's one I'm prepared to live with.

这篇关于使用 ScrollViewer 单击并拖动滚动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆