|
|
|
|
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
- Detect a drag as a combinatination of MouseMove and MouseLeftButtonDown
- Find the data you want to drag and create a DataObject that contains the format, the data and the allowed effects.
- Initiate the dragging by calling DoDragDrop()
- Set the AllowDrop property to True on the elements you want to allow dropping.
- 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.
- 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 List_DragEnter(object sender, DragEventArgs e)
{
if (!e.Data.GetDataPresent("contact") ||
sender == e.Source)
{
e.Effects = DragDropEffects.None;
}
}
private void List_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);
}
}
Comments on this article
Show all comments
 |
| John | |
|
| Commented on 13.August 2009 |
| Ill suck your dick man
|
|
|
 |
| mudboots | |
|
| Commented on 26.August 2009 |
It's a quite simple and helpful example. Thanks for sharing it.
By the way, I think this part:
if (e.LeftButton == MouseButtonState.Pressed &&
Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance &&
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance )
should be
if (e.LeftButton == MouseButtonState.Pressed &&
( Math.Abs(diff.X) > SystemParameters.MinimumHorizontalDragDistance ||
Math.Abs(diff.Y) > SystemParameters.MinimumVerticalDragDistance ) )
What do you think?
|
|
|
 |
| Shiv | |
|
| Commented on 9.September 2009 |
| Can I move(drag) more than one object(UIElement) simultaneously on the canvas. Above code works for single object but how can I do for multiple objects?
|
|
|
 |
| TreeMe | |
|
| Commented on 25.September 2009 |
I tried to change your example to work with a treeview instead of a listview. I get an Exception in the PreviewMouseMove method saying it cannot cast at the ItemContainerGenerator part. Can you show a working TreeView example?
Thanks.
|
|
|
 |
| Rob | |
|
| Commented on 14.October 2009 |
| I don't know what half of this means but you're fu*king cute.
|
|
|
 |
| asaassa | |
|
| Commented on 31.October 2009 |
| asaa
|
|
|
 |
| ss | |
|
| Commented on 4.November 2009 |
| What is 'contact' in above code?
|
|
|
 |
| Joe Schmoe... | |
|
| Commented on 18.November 2009 |
Okay, I'm not sure how well this feed is monitored judging by the comments but I'll give it a shot. This tutorial is helpful to a point but the situation that I am in is a little different than what you have. I am passing data from one control into another. How can I translate the XAML to code on the back end to facilitate the second half of this communication?
<ListView x:Name="DropList"
Drop="DropList_Drop"
DragEnter="DropList_DragEnter"
AllowDrop="True" />
Where I need in the Code customControl.dropList.AllowDrop = true;
customControl.dropList.DragEnter += new DragEventHandler(DropList_DragEnter);
customControl.dropList.Drop += new DragEventHandler(DropList_DragEnter);
|
|
|
 |
| Joe Schmoe... | |
|
| Commented on 19.November 2009 |
Okay, I'm not sure how well this feed is monitored judging by the comments but I'll give it a shot. This tutorial is helpful to a point but the situation that I am in is a little different than what you have. I am passing data from one control into another. How can I translate the XAML to code on the back end to facilitate the second half of this communication?
<ListView x:Name="DropList"
Drop="DropList_Drop"
DragEnter="DropList_DragEnter"
AllowDrop="True" />
Where I need in the Code customControl.dropList.AllowDrop = true;
customControl.dropList.DragEnter += new DragEventHandler(DropList_DragEnter);
customControl.dropList.Drop += new DragEventHandler(DropList_DragEnter);
|
|
|
 |
| GUSTAVO... | |
|
| Commented on 22.November 2009 |
COMO HAGO PARA HACER DRAG AND DROG A UN VIEWPORT3D EN UNA APLICACION WPF PERO NECESITO HACERLO CON LENGUAJE DE VISUAL BASIC.NET CON ESE LENGUAJE NECESITO HACERLO LES AGRADECERIA QUE ME ENVIARAN UN EJEMPLO DE WPF PERO CON VISUAL BASIC.NET
GRACIAS POR LA AYUDA DIOS LES BENDIGA
|
|
|
 |
| amit | |
|
| Commented on 15.December 2009 |
will you please tell that what is Contact in this article
is it user defined or something else
what is it's data type
|
|
|
 |
| abc | |
|
| Commented on 15.December 2009 |
| what is contact in this article??
|
|
|
 |
| PPShoo | |
|
| Commented on 18.December 2009 |
| In this example, Contact is the user defined class to hold the data or set of data that you want to transfer between two controls. You can create new class to fit your need. BTW, this is excellent article. I applied this technique to drag/drop for my treeviews. Have some issue though, after the drop event is handled, somehow the MouseMove event is invoked again and causes error. Anyone any clue?
|
|
|
 |
| srikanthReddy | |
|
| Commented on 22.December 2009 |
As per your example, i Created one listBox and Canvas under DockPanel.ListView contains Multiple ListViewItems (Buttons). My Aim is Drag ListViewItems(Buttons) in to canvas. When i am implemented the logis i the below error is populating.
"Specified Visual is already a child of another Visual or the root of a CompositionTarget" Could you please suggest.
|
|
|
 |
| ram | |
|
| Commented on 30.December 2009 |
| this is wonderful article posted
|
|
|
 |
| kw | |
|
| Commented on 4.January 2010 |
Very nice tutorials, keep up the good work.
You misspelled "ancestor" though, might want to correct that
|
|
|
 |
| Amitha | |
|
| Commented on 4.January 2010 |
| I have a datagrid which has a column header.I am dragging the header to another panel and the header text needs to be used for some purpose.But I am not able to drag the column header out of the grid.i shows the restricted cursor.
|
|
|
 |
| kartik | |
|
| Commented on 20.January 2010 |
I want to select multiple items from Listview and drag drop it to another Listview .Do you have any code for that
Thanks in Advance
|
|
|
 |
| kartik | |
|
| Commented on 20.January 2010 |
I want to select multiple items from Listview and drag drop it to another Listview .Do you have any code for that
Thanks in Advance
|
|
|
 |
| johnny | |
|
| Commented on 29.January 2010 |
mud boots, you are right about the below, I was dragging straight horizontal and thought there was a bug, I should have read all the comments before checking myself. Anyways, I think there is a slight chance this could erroneously trigger the drag drop process... Technically, the startPoint should be Nullable and also should have preview mouse up set it to null and check for it....
|
|
|
 |
| Roel F | |
|
| Commented on 29.January 2010 |
I'll try to clear it up here... Please correct me if I'm wrong:
'Contact' is a custom datatype I assume. It is the data behind the ListViewItem (see comments in the code). If there is just a string in there, use string instead of Contact.
Also, the error 'Specified Visual is already a child of blabla...' is exactly what it says: an element cannot be part of two different parent elements. So add code to remove the element form the first list before it is added to the second. eg: firstList.Items.Remove(item);
Hope this helps...
|
|
|
 |
| Sukanya Reddy | |
|
| Commented on 9.February 2010 |
| very nice article posted. really helped me a lot.thank you very much.
|
|
|
 |
| Studentus Askus | |
|
| Commented on 21.February 2010 |
| Can somebody post examples of Contact?
|
|
|
 |
| RomanK | |
|
| Commented on 3.March 2010 |
Yes, an example for Contact (a class?) would be nice...
great tutorial except from that ;)
|
|
|
 |
| Daniel | |
|
| Commented on 10.March 2010 |
From above code
// Find the data behind the ListViewItem
Contact contact = (Contact)listView.ItemContainerGenerator.
ItemFromContainer(listViewItem);
Here contact is the data class which is the data item of ListView.
Look at below code
-----------------------
private void List_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);
}
}
-------------------------
The Contact class data object will be droped to listview
|
|
|
|
|