• 0

[C#/WPF] Mouse Drag Behavior


Question

Here's a little 5-minute glob of code I hacked together to handle dragging ScrollViewer content around with the mouse (or your finger).

It can also reverse the axes for "natural scrolling." There is also a Tolerance setting that is supposed to prevent it from scrolling if you only drag it a little bit. I did this because my crappy touchscreen driver has a lot of jitter.

I claim no responsibility if it explodes or otherwise has undesired effects. :)

using System;
using System.Windows;
using System.Windows.Input;
using System.Windows.Controls;

namespace MouseExtensions
{
    public class MouseDragBehavior
    {
        public static readonly DependencyProperty ToleranceProperty = DependencyProperty.RegisterAttached("Tolerance", typeof(double), typeof(MouseDragBehavior), new FrameworkPropertyMetadata((double)10));
        public static double GetTolerance(DependencyObject d)
        {
            return (double)d.GetValue(ToleranceProperty);
        }

        public static void SetTolerance(DependencyObject d, double value)
        {
            d.SetValue(ToleranceProperty, value);
        }

        public static readonly DependencyProperty ReverseAxisProperty = DependencyProperty.RegisterAttached("ReverseAxis", typeof(bool), typeof(MouseDragBehavior), new FrameworkPropertyMetadata((bool)false));
        public static bool GetReverseAxis(DependencyObject d)
        {
            return (bool)d.GetValue(ReverseAxisProperty);
        }

        public static void SetReverseAxis(DependencyObject d, bool value)
        {
            d.SetValue(ReverseAxisProperty, value);
        }

        private static readonly DependencyProperty DragStartPointProperty = DependencyProperty.RegisterAttached("DragStartPoint", typeof(Point), typeof(MouseDragBehavior), new FrameworkPropertyMetadata((Point)new Point()));
        private static Point GetDragStartPoint(DependencyObject d)
        {
            return (Point)d.GetValue(DragStartPointProperty);
        }

        private static void SetDragStartPoint(DependencyObject d, Point value)
        {
            d.SetValue(DragStartPointProperty, value);
        }

        private static readonly DependencyProperty DragLastPointProperty = DependencyProperty.RegisterAttached("DragLastPoint", typeof(Point), typeof(MouseDragBehavior), new FrameworkPropertyMetadata((Point)new Point()));
        private static Point GetDragLastPoint(DependencyObject d)
        {
            return (Point)d.GetValue(DragLastPointProperty);
        }

        private static void SetDragLastPoint(DependencyObject d, Point value)
        {
            d.SetValue(DragLastPointProperty, value);
        }

        public static readonly DependencyProperty HandleDragProperty = DependencyProperty.RegisterAttached("HandleDrag", typeof(bool), typeof(MouseDragBehavior), new FrameworkPropertyMetadata((bool)false, new PropertyChangedCallback(OnHandleDragChanged)));
        public static bool GetHandleDrag(DependencyObject d)
        {
            return (bool)d.GetValue(HandleDragProperty);
        }

        public static void SetHandleDrag(DependencyObject d, bool value)
        {
            d.SetValue(HandleDragProperty, value);
        }

        private static void OnHandleDragChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ScrollViewer scrollViewer = d as ScrollViewer;
            if ((bool)e.NewValue)
            {
                scrollViewer.PreviewMouseDown += new MouseButtonEventHandler(target_PreviewMouseDown);
                scrollViewer.PreviewMouseMove += new MouseEventHandler(target_PreviewMouseMove);
                scrollViewer.PreviewMouseUp += new MouseButtonEventHandler(target_PreviewMouseUp);
                scrollViewer.MouseLeave += new MouseEventHandler(target_MouseLeave);
            }
            else
            {
                scrollViewer.PreviewMouseDown -= new MouseButtonEventHandler(target_PreviewMouseDown);
                scrollViewer.PreviewMouseMove -= new MouseEventHandler(target_PreviewMouseMove);
                scrollViewer.PreviewMouseUp -= new MouseButtonEventHandler(target_PreviewMouseUp);
                scrollViewer.MouseLeave -= new MouseEventHandler(target_MouseLeave);
            }
        }

        private static bool IsDragging(ScrollViewer scrollViewer)
        {
            Point posNow = Mouse.GetPosition(scrollViewer);
            return ((Math.Abs(GetDragStartPoint(scrollViewer).X - posNow.X) > GetTolerance(scrollViewer)) || (Math.Abs(GetDragStartPoint(scrollViewer).Y - posNow.Y) > GetTolerance(scrollViewer)));
        }

        private static void target_PreviewMouseDown(object sender, MouseButtonEventArgs e)
        {
            var scrollViewer = (ScrollViewer)sender;
            var mousePos = e.GetPosition(scrollViewer);
            scrollViewer.CaptureMouse();

            if (GetDragLastPoint(scrollViewer) == new Point())
            {
                SetDragStartPoint(scrollViewer, mousePos);
                SetDragLastPoint(scrollViewer, mousePos);
            }

            if (IsDragging(scrollViewer))
            {
                e.Handled = true;
                return;
            }
            else
                scrollViewer.ReleaseMouseCapture();
        }

        private static void target_PreviewMouseMove(object sender, MouseEventArgs e)
        {
            var scrollViewer = (ScrollViewer)sender;
            if (GetDragLastPoint(scrollViewer) != new Point())
            {
                Point posNow = e.GetPosition(scrollViewer);

                double dX = posNow.X - GetDragLastPoint(scrollViewer).X;
                double dY = posNow.Y - GetDragLastPoint(scrollViewer).Y;

                SetDragLastPoint(scrollViewer, posNow);

                if (!IsDragging(scrollViewer))
                    return;

                if (GetReverseAxis(scrollViewer))
                {
                    scrollViewer.ScrollToHorizontalOffset(scrollViewer.HorizontalOffset + dX);
                    scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset + dY);
                }
                else
                {
                    scrollViewer.ScrollToHorizontalOffset(scrollViewer.HorizontalOffset - dX);
                    scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - dY);
                }
            }

            e.Handled = true;
        }

        private static void target_PreviewMouseUp(object sender, MouseButtonEventArgs e)
        {
            var scrollViewer = (ScrollViewer)sender;
            if (GetDragLastPoint(scrollViewer) != new Point() && IsDragging(scrollViewer))
            {
                e.Handled = true;
                scrollViewer.ReleaseMouseCapture();
            }

            Reset(scrollViewer);
        }

        private static void target_MouseLeave(object sender, MouseEventArgs e)
        {
            var scrollViewer = (ScrollViewer)sender;
            Reset(scrollViewer);
        }

        private static void Reset(ScrollViewer scrollViewer)
        {
            SetDragLastPoint(scrollViewer, new Point());
            SetDragStartPoint(scrollViewer, new Point());
        }
    }
}

To use it simply reference the namespace and set MouseDragBehavior.HandleDrag to true in your

ScrollViewer declaration:

            <ScrollViewer
                xmlns:my="clr-namespace:MouseExtensions"
                my:MouseDragBehavior.HandleDrag="True"
                my:MouseDragBehavior.ReverseAxis="True" /> 

Link to comment
Share on other sites

3 answers to this question

Recommended Posts

  • 0

Do you have any plans to update this to work for Windows 8 apps?

Windows 8 WinRT applications come with dragging behaviour out of the box within scrolling lists.

Link to comment
Share on other sites

This topic is now closed to further replies.
  • Recently Browsing   0 members

    • No registered users viewing this page.