Bookmark and Share Share...    Subscribe to this feed Feed   About Christian Moser  


Drag and Drop in WPF

Introduction

Drag&Drop can drasticly improve the productiviy and user experience of a software. But only a few programmers provide drag and drop functionality in their applications, because they think its much more dificult than it really is. This article shows how simple drag and drop can be implemented in WPF.

Drag&Drop in 6 Steps

  1. Detect a drag as a combinatination of MouseMove and MouseLeftButtonDown
  2. Find the data you want to drag and create a DataObject that contains the format, the data and the allowed effects.
  3. Initiate the dragging by calling DoDragDrop()
  4. Set the AllowDrop property to True on the elements you want to allow dropping.
  5. Register a handler to the DragEnter event to detect a dragging over the drop location. Check the format and the data by calling GetDataPresent() on the event args. If the data can be dropped, set the Effect property on the event args to display the appropriate mouse cursor.
  6. When the user releases the mouse button the DragDrop event is called. Get the data by calling the GetData() method on the Data object provided in the event args.

...and that's all the magic.

Drag

To start the drag operation, we have to detect a mouse move while the left mouse button is pressed. To do this we have to hook up handlers on the PreviewMouseMove and PreviewMouseLeftButtonDown events.

To prevent occasionally drags, its a good design to not start the drag operation until the user has moved the mouse cursor by a couple of pixels. WPF provides a constant that contains the amount of pixel that Windows uses.

When the drag is initiated, we need to specify the data we want to drag. In our case its the data of the ListViewItem we dragged. We find the ListViewItem in the OriginalSource of the mouse event args. By calling ItemContainerGenerator.ItemFromContainer we get the data behind the ListViewItem.

Create a DataObject to transport the data to the drop location. The constructor takes two arguments. A string that describes the format and the data we want to drag.


 
<ListView x:Name="DragList" 
          PreviewMouseLeftButtonDown="List_PreviewMouseLeftButtonDown" 
          PreviewMouseMove="List_MouseMove"/>
 
 
 
private void List_PreviewMouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    // Store the mouse position
    startPoint = e.GetPosition(null);
}
 
 
 
private void List_MouseMove(object sender, MouseEventArgs e)
{
    // Get the current mouse position
    Point mousePos = e.GetPosition(null);
    Vector diff = startPoint - mousePos;
 
    if (e.LeftButton == MouseButtonState.Pressed &&
        Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
        Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance )
    {
        // Get the dragged ListViewItem
        ListView listView = sender as ListView;
        ListViewItem listViewItem = 
            FindAnchestor<ListViewItem>((DependencyObject)e.OriginalSource);
 
        // Find the data behind the ListViewItem
        Contact contact = (Contact)listView.ItemContainerGenerator.
            ItemFromContainer(listViewItem);
 
        // Initialize the drag & drop operation
        DataObject dragData = new DataObject("myFormat", contact );
        DragDrop.DoDragDrop(listViewItem, dragData, DragDropEffects.Move);
    } 
}
 
 
 
// Helper to search up the VisualTree
private static T FindAnchestor<T>(DependencyObject current)
    where T : DependencyObject
{
    do
    {
        if( current is T )
        {
            return (T)current;
        }
        current = VisualTreeHelper.GetParent(current);
    }
    while (current != null);
    return null;
}
 
 

Drop

To make an element be a drop location, set the AllowDrop property to true. When the user drags an item over the element, the DragEnter event is called. In this event you can analyze the data and decide if a drop is allowed or not.

When the user releases the mouse button the Drop event is called. The data is available in the DataObject provided in the DragEventArgs.


 
<ListView x:Name="DropList" 
          Drop="DropList_Drop" 
          DragEnter="DropList_DragEnter" 
          AllowDrop="True" />
 
 
 
private void DropList_DragEnter(object sender, DragEventArgs e)
{
    if (!e.Data.GetDataPresent("myFormat") ||
        sender == e.Source)
    {
        e.Effects = DragDropEffects.None;
    }
}
 
 
 
private void DropList_Drop(object sender, DragEventArgs e)
{
    if (e.Data.GetDataPresent("myFormat"))
    {
        Contact contact = e.Data.GetData("myFormat") as Contact;
        ListView listView = sender as ListView;
        listView.Items.Add(contact);
    }
}
 
 




Last modified: 2011-04-19 16:59:14
Copyright (c) by Christian Moser, 2011.

 Comments on this article

Show all comments
David C
Commented on 17.June 2011
I tried the same thing in the RightButton click but at that time DragOver event is not fired.. How can i resolve this problem ?
ZeePrime
Commented on 17.June 2011
Very Good.

Warning with the if, you miss a couple of brackets as &amp;&amp; has precedence over ||
if (e.LeftButton == MouseButtonState.Pressed &amp;&amp;
Math.Abs(diff.X) &gt; SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(diff.Y) &gt; SystemParameters.MinimumVerticalDragDistance )
kamlendra
Commented on 23.June 2011
how can i go for multiple drag and drop?? say i want to select three items @ a time from Drag_list and drop them into Drop_list.
oc
Commented on 27.June 2011
Hi,
I am trying out your code but in Datagrid not in Listview. I couldn't figure it out the 'Contact' in this line
Contact contact = (Contact)listView.ItemContainerGenerator.
what does it stands for ?
Regards
david
Commented on 20.July 2011
I agree with Olivier as for the missing parentheses in the mouseMove conditions. You should correct it in the main article :p
NIkhil
Commented on 21.July 2011
Hi Christian,

Please elaborate what is Contact used in 4 places in this example?
Contact contact = (Contact)listView.ItemContainerGenerator.ItemFromContainer(listViewItem);
Contact contact = e.Data.GetData(&quot;myFormat&quot;) as Contact;

Thanks in advance
KornMuffin
Commented on 28.July 2011
Sweet and simple. Thanks for the wealth of info on your site.

@oc - just use a string instead of the Contact class if you don't want to create your own Contact class.
tooma
Commented on 2.August 2011
i can smell poo and a small amount of wee too.
tooma
Commented on 2.August 2011
i can smell poo and a small amount of wee too.
seiko
Commented on 4.August 2011
Why i am getting this exception everytime?
Element already has a logical parent. It must be detached from the old parent before it is attached to a new one.

on line
listView.Items.Add(contact); ??
Somebody may know that?
bizzare
Commented on 17.August 2011
Hi,
Im a newbie to WPF and i dont know if this question even makes sense here but i dint know where else to post it! I need to select two or more items from a list box and then enable a button. Can you please help me as to how i could do it?Thanks!
bizzare
Commented on 17.August 2011
Hi,
Im a newbie to WPF and i dont know if this question even makes sense here but i dint know where else to post it! I need to select two or more items from a list box and then enable a button. Can you please help me as to how i could do it?Thanks!
Abhishek...
Commented on 17.August 2011
I m using very similar drag and drop functionality in my WPF app , while i wish to drag item from combobox to textbox , when im doing so , the combobox always get closed and i can't select any items from it to drag towards textbox.
bizzare
Commented on 17.August 2011
Hi,
Im a newbie to WPF and i dont know if this question even makes sense here but i dint know where else to post it! I need to select two or more items from a list box and then enable a button. Can you please help me as to how i could do it?Thanks!
bizzare
Commented on 17.August 2011
Hi,
Im a newbie to WPF and i dont know if this question even makes sense here but i dint know where else to post it! I need to select two or more items from a list box and then enable a button. Can you please help me as to how i could do it?Thanks!
Tom Pester
Commented on 21.August 2011
Contact is just a class (domain/poco)

public class Contact
{
public Contact(string firstname, string lastname)
{
Firstname = firstname;
Lastname = lastname;
}

public string Firstname { get; set; }
public string Lastname { get; set; }

public override string ToString()
{
return string.Format(&quot;Firstname: {0}, Lastname: {1}&quot;, Firstname, Lastname);
}
}

To get the code working I had to change the code from (this is a bug IMO)


if (e.LeftButton == MouseButtonState.Pressed &amp;&amp;
Math.Abs(diff.X) &gt; SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(diff.Y) &gt; SystemParameters.MinimumVerticalDragDistance
)
to
if (e.LeftButton == MouseButtonState.Pressed &amp;&amp;
(
Math.Abs(diff.X) &gt; SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(diff.Y) &gt; SystemParameters.MinimumVerticalDragDistance
)
)

I also got null reference exceptions and had to check the listViewItem for null:

ListViewItem listViewItem =
FindAnchestor&lt;ListViewItem&gt;((DependencyObject) e.OriginalSource);
if (listViewItem == null) return;

I think its due to a timing issue if you do a drag and drop very fast or execute the code on a fast computer?

Thanks for this minimal sample. It's excellent to get started from.
Lasoty
Commented on 22.August 2011
Hi!
Please show mi how look the Contact class.

TXH
Lasoty
Commented on 22.August 2011
Hi!
Please show mi how look the Contact class.

TXH
Guest
Commented on 5.September 2011
Hi!!
I am able to drag the control. but i have to drop the control to dynamically created Panel or Canvas. How i should proceed? Pls help me
Guest
Commented on 5.September 2011
Hi!!
I am able to drag the control. but i have to drop the control to dynamically created Panel or Canvas. How i should proceed? Pls help me
Guest
Commented on 5.September 2011
Hi!!
I am able to drag the control. but i have to drop the control to dynamically created Panel or Canvas. How i should proceed? Pls help me
Guest
Commented on 5.September 2011
Hi!!
I am able to drag the control. but i have to drop the control to dynamically created Panel or Canvas. How i should proceed? Pls help me
Guest
Commented on 5.September 2011
Hi!!
I am able to drag the control. but i have to drop the control to dynamically created Panel or Canvas. How i should proceed? Pls help me
Raman
Commented on 23.September 2011
Hi,

I have tried this program, just used the class &quot;People&quot; instead of &quot;Contact&quot; and it wors fine. Here is the code that I have tried for the &quot;People&quot;:

public class Person

{


private string _name;
private string _relationshipType;
public string Name
{
get { return _name; }
set
{
_name = value;
}
}
public string RelationshipType
{
get { return _relationshipType; }
set { _relationshipType = value; }
}
public Person(string name, string relationshiptype)
{
this._name = name;
this._relationshipType = relationshiptype;
}

}
public class People : ObservableCollection&lt;Person&gt;
{
public People()
{
Add(new Person(&quot;Sean&quot;, &quot;Friend&quot;));
Add(new Person(&quot;Chris&quot;, &quot;Friend&quot;));
Add(new Person(&quot;Peter&quot;, &quot;Brother&quot;));
Add(new Person(&quot;Jeremy&quot;, &quot;Brother&quot;));
Add(new Person(&quot;Kevin&quot;, &quot;Brother&quot;));
}
}

Regards
Patrick
Commented on 27.September 2011
Hello,
I tried to implement this and the button state is never pressed. Also my Clickevents don't trigger anymore. So drag and drop works or the Clickevent never both.
regards

Name
E-Mail (optional)
Comment