//! \file DragScroll.cs //! \date Sun Jul 06 10:47:20 2014 //! \brief Scroll control contents by dragging. // // http://matthamilton.net/touchscrolling-for-scrollviewer using System.Collections.Generic; using System.Windows.Controls; using System.Windows; using System.Windows.Input; using System.Windows.Media; using System.Diagnostics; namespace GARbro.GUI { 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 static Cursor GetDraggingCursor (UIElement e) { return (Cursor)e.GetValue (DraggingCursorProperty); } public static void SetDraggingCursor (UIElement e, Cursor value) { e.SetValue (DraggingCursorProperty, value); } public bool IsEnabled { get { return (bool)GetValue(IsEnabledProperty); } set { SetValue(IsEnabledProperty, value); } } public Cursor DraggingCursor { get { return (Cursor)GetValue (DraggingCursorProperty); } set { SetValue (DraggingCursorProperty, value); } } public static readonly DependencyProperty IsEnabledProperty = DependencyProperty.RegisterAttached("IsEnabled", typeof(bool), typeof(TouchScrolling), new UIPropertyMetadata(false, IsEnabledChanged)); public static readonly DependencyProperty DraggingCursorProperty = DependencyProperty.RegisterAttached("DraggingCursor", typeof(Cursor), typeof(TouchScrolling)); static Dictionary _captures = new Dictionary(); static void IsEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) { var target = d as FrameworkElement; 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 FrameworkElement; if (null == target) 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 FrameworkElement; if (null == target) return; var scroller = FindVisualParent (target); if (null == scroller) { Trace.WriteLine ("Control should be placed inside ScrollViewer for drag scrolling to work"); return; } _captures[sender] = new MouseCapture { HorizontalOffset = scroller.HorizontalOffset, VerticalOffset = scroller.VerticalOffset, Point = e.GetPosition(scroller), }; var cursor = target.GetValue (DraggingCursorProperty) as Cursor; if (cursor != null) Mouse.OverrideCursor = cursor; } static void target_Loaded(object sender, RoutedEventArgs e) { var target = sender as FrameworkElement; if (target == null) return; // Debug.WriteLine("DragScroll target Loaded", sender); 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 FrameworkElement; if (target == null) return; target.ReleaseMouseCapture(); Mouse.OverrideCursor = null; } 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 FrameworkElement; if (null == target) return; var scroller = FindVisualParent (target); if (null == scroller) return; var capture = _captures[sender]; var point = e.GetPosition (scroller); var dx = point.X - capture.Point.X; var dy = point.Y - capture.Point.Y; if (System.Math.Abs(dy) > 5 || System.Math.Abs(dx) > 5) { target.CaptureMouse(); } scroller.ScrollToHorizontalOffset(capture.HorizontalOffset - dx); scroller.ScrollToVerticalOffset(capture.VerticalOffset - dy); } static parentItem FindVisualParent (DependencyObject obj) where parentItem : DependencyObject { if (null == obj) return null; DependencyObject parent = VisualTreeHelper.GetParent (obj); while (parent != null && !(parent is parentItem)) { parent = VisualTreeHelper.GetParent (parent); } return parent as parentItem; } internal class MouseCapture { public double HorizontalOffset { get; set; } public double VerticalOffset { get; set; } public Point Point { get; set; } } } }