WPF Drag & Drop: How to detect when drag is started

This article will explain how to detect when drag is started in WPF Applications.

Features:
1) Reliably detect drag & drop
2) Don't confuse double clicks as drag start
3) Don't confuse scrolling as drag event 

Notes:
- WPF is very powerful, but one thing i find missing is an event telling developers when drag is started
- The solution here involves handling Left Button Down, Mouse Move & Left Button Up event combinations to detect when drag & drop is started and when it ended

Important:
Even though the code posted here uses code-behind, I highly recommend using MVVM way of handling drag & drop using attached properties on controls to detect drag & drop, show visual feedback and transfer data. (I will post a WPF MVVM Drag & Drop Framework soon)
XAML:
<Window x:Class="DragAndDropDragStart.MainWindow" 
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
        Title="WPF Drag and Drop: detect when drag is started" 
        Height="200" Width="200">
    <Grid>
        <TextBlock Text="Drag me!" 
                   HorizontalAlignment="Center" 
                   VerticalAlignment="Center" 
                   FontSize="25" Foreground="RoyalBlue" 
                   MouseLeftButtonDown="OnDragSourceMouseLeftButtonDown" 
                   MouseLeftButtonUp="OnDragSourceMouseLeftButtonUp" 
                   MouseMove="OnDragSourceMouseMove"/>
    </Grid>
</Window>

Code-Behind:
 using System;  
 using System.Windows;  
 using System.Windows.Controls.Primitives;  
 using System.Windows.Input;  
 using System.Windows.Media;  
 namespace DragAndDropDragStart  
 {  
   /// <summary>  
   /// Interaction logic for MainWindow.xaml  
   /// </summary>  
   public partial class MainWindow  
   {  
     private Point _dragStartPoint;  
     private bool _isMouseDown;  
     public MainWindow()  
     {  
       InitializeComponent();  
     }  
     private void OnDragSourceMouseLeftButtonDown(object sender,   
       MouseButtonEventArgs e)  
     {  
       if (e.ClickCount != 1)  
       {  
         return;  
       }  
       _dragStartPoint = e.GetPosition(e.Source as FrameworkElement);  
       _isMouseDown = true;  
     }  
     private void OnDragSourceMouseMove(object sender, MouseEventArgs e)  
     {  
       if (_isMouseDown &&   
         IsDragGesture(e.GetPosition(e.Source as FrameworkElement)))  
       {  
         if (ReferenceEquals(null,   
           FindAncestor<ScrollBar>((DependencyObject)e.OriginalSource)))  
         {  
           // Drag Started  
           _isMouseDown = false;  
           Mouse.Capture(sender as UIElement);  
           MessageBox.Show("Drag Started");  
         }  
       }  
     }  
     private void OnDragSourceMouseLeftButtonUp(object sender,   
       MouseButtonEventArgs e)  
     {  
       _isMouseDown = false;  
       Mouse.Capture(null);  
     }  
     private bool IsDragGesture(Point point)  
     {  
       var horizontalMove = Math.Abs(point.X - _dragStartPoint.X);  
       var verticalMove = Math.Abs(point.Y - _dragStartPoint.Y);  
       var hGesture = horizontalMove >  
               SystemParameters.MinimumHorizontalDragDistance;  
       var vGesture = verticalMove >  
               SystemParameters.MinimumVerticalDragDistance;  
       return (hGesture | vGesture);  
     }  
     private static T FindAncestor<T>(DependencyObject dependencyObject)  
       where T : DependencyObject  
     {  
       var parent = VisualTreeHelper.GetParent(dependencyObject);  
       if (parent == null) return null;  
       var parentT = parent as T;  
       return parentT ?? FindAncestor<T>(parent);  
     }  
   }  
 }  





Comments

Popular posts from this blog

WPF How to Dispose ViewModel when the associated UserControl (Not Window) closes?

C# How to unit test Dispatcher

WPF: How to Deep Copy WPF object (e.g. UIElement) ?