Wednesday, December 12, 2007

Drag and Drop in WPF

Drag and Drop in WPF is, I think, more complex than it should be. I've had to implement it a few times now, so I recently put together a simple helper class that simplifies things.

The full class code follows at the end of the post, but first I will give a few examples of using it. If you have an object that you want to be draggable, just create a DragDropHander object for it:
DragDropHandler dragDrop = new DragDropHandler(myControl, new System.Windows.DataObject(myDragData));
Where "myControl" is the WPF control the use will drag from, and "myDragData" is the object you wish to receive on the other end of the drag and drop. To receive the drop, make sure the control you want to drop to has AllowDrop set, and add a drop handler in your control initialization:
MyControl.Drop += new DragEventHandler(MyControl_Drop);

Implement the handler like this:
void MyControl_Drop(object sender, DragEventArgs e)
{
MyDataType myData = (MyDataType )e.Data.GetData(typeof(MyDataType).ToString());

// Do something with the dropped object
}
If you want to drag an object outside of your application, you need to create the appropriate DataObject. A common case is a filename. To create a drag handler that drags a filename, do:
DragDropHandler dragDrop = new DragDropHandler(myControl, new System.Windows.DataObject(DataFormats.FileDrop, new string[] { myPath }));
You can obviously get much more complicated with drag and drop - handling drag enter/over/leave events and using adorners to visually display drag data, for example. This should get you started, though, and probably is sufficient for most needs.

Here is the full DragDropHandler class implementation:
using System;
using System.Windows.Data;
using System.Windows;
using System.Windows.Input;

namespace MyCoolNamespace
{
public class DragDropHandler
{
private FrameworkElement dragElement = null;
private bool dragging = false;
private bool inDragDrop = false;
private Point dragStart;
private DataObject dataObject = null;

public DragDropHandler(FrameworkElement dragElement, DataObject dataObject)
{
this.dragElement = dragElement;
this.dataObject = dataObject;

dragElement.MouseLeftButtonDown += new MouseButtonEventHandler(dragElement_MouseLeftButtonDown);
dragElement.MouseMove += new MouseEventHandler(dragElement_MouseMove);
}

void dragElement_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
if (!dragElement.IsMouseCaptured)
{
dragging = true;
dragStart = e.GetPosition(dragElement);
}
}

void dragElement_MouseMove(object sender, MouseEventArgs e)
{
if (dragging)
{
Point currentPos = e.GetPosition(dragElement);

if ((Math.Abs(currentPos.X - dragStart.X) > 5) || (Math.Abs(currentPos.Y - dragStart.Y) > 5))
{
dragElement.CaptureMouse();

inDragDrop = true;
DragDropEffects de = DragDrop.DoDragDrop(dragElement, dataObject, DragDropEffects.Move);
inDragDrop = false;
dragging = false;
dragElement.ReleaseMouseCapture();
}
}
}
}
}

6 comments:

  1. nice, but your class is mixing two entirely different things. starting a dragdrop action is the one thing, and it is easy. what you do is reacting on various mouse events to see if a mouse-drag actually happened. I'd put this in a DragController that allows generic reuse of the mouse-drag code and then start a dragdrop action if the DragController fires its Drag event.
    -- henon

    ReplyDelete
  2. hi,
    can you please provide a real example of this for how to use this class for drag and drop controls in canvas.

    ReplyDelete
  3. nice and thank you for your sharing

    ReplyDelete
  4. "dragging = false; " should before
    "DragDropEffects de = DragDrop.DoDragDrop(...)", otherwise application will hang.

    Function Eight

    ReplyDelete
  5. i tried to use the same in VB . I used ur code ,but i;m getting an error in the following lines

    dragElement.MouseLeftButtonDownEvent += New MouseButtonEventHandler(AddressOf dragElement_MouseLeftButtonDown)
    dragElement.MouseMoveEvent += New MouseEventHandler(AddressOf dragElement_mouseMove)

    Error: Access of shared member, constant member, enum member or nested type through an instance; qualifying expression will not be evaluated.

    You have any idea of this error

    ReplyDelete