Click here to Skip to main content
Click here to Skip to main content

Drag and Drop in WPF

, 7 Nov 2009
Rate this:
Please Sign up or sign in to vote.
An article showing how to add drag and drop to a WPF application using the GongSolutions.Wpf.DragDrop library.

Introduction

For a while, I've been lamenting the lack of decent drag and drop support in WPF. While WPF has advanced desktop GUI programming considerably from the days of WinForms, drag and drop hasn't changed since I started programming on Windows with Visual Basic 3.0.

In particular, one must litter code-behinds with drag and drop logic, something that is anathema to any self-respecting WPF developer in the days of MVVM.

After putting up with this for a while, I came across an article by Bea Stollnitz on using attached properties to add drag and drop logic to controls in XAML. However, Bea's solution did not go as far as I needed for my purposes. What I needed was:

  • MVVM support: Anything more than basic drag drop operations need logic, and code-behind isn't the right place for that logic. Decisions about what can be dragged and what can be dropped where should be delegated to ViewModels.
  • Insert/Drop Into: Sometimes when you drag an item from one place to another, you're re-ordering the items in a list. At other times, you're dropping one item onto another, like dropping a file into a folder in a file manager.
  • TreeView support
  • Multiple selections
  • Automatic scrolling

Looking around, I couldn't see anything that satisfied my needs, so I decided to write one. You can find the latest source code with examples at the Google Code project. It's licensed under the BSD license, so you're free to use it pretty much however you like.

Using the Code

To demonstrate the use of the drag drop framework, let's use a simple Schools example. In this example, we have three ViewModels:

  • MainViewModel: This simply contains the collection of schools.
  • SchoolViewModel: A school has a name and a collection of pupils.
  • PupilViewModel: A pupil has a name.

In our UI, we'll display two listboxes. The first to display the list of schools, and the second to display the list of pupils belonging to that school. Our XAML looks like this:

<Window.DataContext>
    <local:MainViewModel/>
</Window.DataContext>

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition/>
        <ColumnDefinition/>
    </Grid.ColumnDefinitions>
    
    <ListBox Grid.Column="0" 
       ItemsSource="{Binding Schools}" 
       DisplayMemberPath="Name" 
       IsSynchronizedWithCurrentItem="True"/>
    <ListBox Grid.Column="1" 
       ItemsSource="{Binding Schools.CurrentItem.Pupils}" 
       DisplayMemberPath="FullName"/>
</Grid>

And the resulting window looks like this:

gong-wpf-dragdrop1.png

Firstly, we want to be able to re-order the pupils in the second ListBox. This is done by setting the IsDragSource and IsDropTarget attached properties on the ListBox:

<ListBox Grid.Column="1" 
  ItemsSource="{Binding Schools.CurrentItem.Pupils}" 
  DisplayMemberPath="FullName"
  dd:DragDrop.IsDragSource="True" 
  dd:DragDrop.IsDropTarget="True"/>

You will now be able to drag and drop items within the ListBox to re-order them. Now, you might suppose that if you were to set the IsDropTarget property on the first ListBox, you'd be able to drag a pupil into a school. However, try doing this, and you won't be allowed. That's because the first ListBox is bound to a collection of SchoolViewModel objects whereas the second ListBox is bound to a collection of PupilViewModels. What should happen here? The framework doesn't know, so we're going to have to tell it.

Enter DropHandlers

To add a drop handler to the ListBox, we set the DropHandler attached property on the first ListBox, and bind it to a ViewModel. In this case, we'll just bind it to the MainViewModel, like so:

<ListBox Grid.Column="0" 
  ItemsSource="{Binding Schools}" DisplayMemberPath="Name" 
  IsSynchronizedWithCurrentItem="True"
  dd:DragDrop.IsDropTarget="True" dd:DragDrop.DropHandler="{Binding}"/>

Now we need to add the handling code to MainViewModel. To do this, we implement the IDropTarget interface. This interface defines two methods: DragOver and Drop. DragOver is called whilst the drag is underway, and is used to determine whether the current drop target is valid:

void IDropTarget.DragOver(DropInfo dropInfo)
{
    if (dropInfo.Data is PupilViewModel && 
        dropInfo.TargetItem is SchoolViewModel)
    {
        dropInfo.DropTargetAdorner = DropTargetAdorners.Highlight;
        dropInfo.Effects = DragDropEffects.Move;
    }
}

Here, we're checking that the dragged data is a PupilViewModel, and the item that is currently the target of the drag is a SchoolViewModel.

The dropInfo.Data property contains the data being dragged. If the control that was the source of the drag is a bound control (which it is), this will be the object that the dragged item was bound to. The dropInfo.TargetItem property contains the object that the item currently under the mouse cursor is bound to.

If the data types are correct, we go ahead and set the drop target adorner. Because dropping a pupil into a school causes the pupil to be added to the school, we choose the Highlight adorner which highlights the target item. The other available drop target adorner is the Insert adorner, which draws a caret at the point in the list the dropped item will be inserted, and is what you see when re-ordering pupils in the second ListBox.

Finally, we set the drag effect to DragDropEffects.Move. This tells the framework that the dragged data can be dropped here and to display a Move mouse pointer. If we don't set this property, the default value of DragDropEffects.None is used, which indicates that a drop is not allowed at this position.

Next, the Drop method:

void IDropTarget.Drop(DropInfo dropInfo)
{
    SchoolViewModel school = (SchoolViewModel)dropInfo.TargetItem;
    PupilViewModel pupil = (PupilViewModel)dropInfo.Data;
    school.Pupils.Add(pupil);
}

Here, we simply get the SchoolViewModel and the PupilViewModel that are involved in the drop from the DropInfo parameter, and add the pupil to the school. We can be sure that the data is going to be of the expected type because the DragOver method has already filtered out any other situation.

Oh! But hold on, when we drop the pupil into the new school, we're not removing him from the previous school, i.e., we're copying instead of moving. To remove the pupil from the previous school, we need to be able to get hold of the collection from which the drag was initiated. This is provided by the DropInfo.DragInfo object. This object holds information relating to the source of the drag. In particular, we're interested in the SourceCollection property:

((IList)dropInfo.DragInfo.SourceCollection).Remove(pupil);

That's All For Now Folks

That completes this introductory article. You can find more examples in the Google Code project. Some features to look out for:

  • Drag handlers
  • Drop adorners
  • Multiple selections

If this article interested you, please join in! Post to the Google Groups, or contribute code to advance the project, and hopefully, we can make something that can be drag and dropped screaming out of the 20th century.

License

This article, along with any associated source code and files, is licensed under The BSD License

Share

About the Author

Steven Kirk

United Kingdom United Kingdom
No Biography provided

Comments and Discussions

 
GeneralAwesome control! PinmemberJonHarder29-Jul-14 14:47 
QuestionCan drop onto canvas but can't figure out how to pick the dropped item up again PinmemberMember 1066774913-Mar-14 5:27 
QuestionProblem with dragging from outside of application PinmemberSaadh6666-Feb-14 3:19 
GeneralGreat! PinmemberHamed Mosavi24-Dec-13 9:37 
GeneralMy vote of 4 Pinmembershashank7685-Sep-13 23:59 
GeneralMy vote of 5 PinmemberKim Togo29-Aug-13 3:10 
QuestionDragging and dropping to and from a usercontrol and an itemscontrol PinmemberLooLooClement17-Aug-13 4:09 
GeneralA thing of beauty PinmemberDijj7-Jun-13 23:19 
Questioncan't unselect item from mutiple selected items in ListBox Pinmembersrikanth.chippa5-Mar-13 0:55 
QuestionIssue with TabControl PinmemberCoxianuk27-Feb-13 6:59 
GeneralMy vote of 5 PinmentorWayne Gaylard4-Jan-13 0:45 
GeneralMy vote of 5 PinmemberJecho Jekov30-Sep-12 19:43 
Questiondon't work correctly Pinmembermichaelgr130-Jun-12 21:02 
Questiondragging to a child in a treeview Pinmemberboogster71427-Mar-12 1:05 
QuestionCan't Get It To Work PinmemberKevin Marois1-Mar-12 12:29 
GeneralMy vote of 5 PinmemberMember 867759026-Feb-12 3:14 
QuestionExcellent! Pinmembercaliban2621-Jan-12 0:37 
QuestionGREAT! PinmemberMember 84445886-Jan-12 9:53 
GeneralMy vote of 5 Pinmemberalfredjkwack27-Nov-11 16:29 
GeneralMy vote of 4 Pinmemberearvella20-Nov-11 20:17 
QuestionWhat to do if Droptarget is itunes... Pinmembergoodreason13-Oct-11 9:51 
BugReordering the Schools ListBox ? PinmemberRaul Dsouza16-Aug-11 23:34 
NewsI just posted a project that uses your drag drop tool. PinmemberDannyStaten13-Aug-11 5:29 
QuestionDrap n Drop Pinmemberc#_cool25-Jul-11 7:23 
QuestionCan DropTarget be a canvas? PinmemberIman A18-Jul-11 19:52 
GeneralRe: Can DropTarget be a canvas? PinmemberThomas_M15-Aug-11 2:34 
AnswerRe: Can DropTarget be a canvas? PinmemberThomas_M7-Sep-11 4:14 
GeneralRe: Can DropTarget be a canvas? PinmemberIman A7-Sep-11 13:43 
Generalwith VS2010 anf Wpf 4.0 Pinmemberanthride8-Jun-11 6:40 
GeneralGood work Pinmemberanthride8-Jun-11 6:32 
Questionlicense? PinmemberMember 767239314-Feb-11 5:35 
GeneralHaving trouble with TreeView Functionality PinmemberOffApps20-Jan-11 5:02 
GeneralMy vote of 5 Pinmemberstooboo29-Nov-10 5:47 
Generaldrag not working in wpf control hosted in win forms PinmemberDaemon44429-Oct-10 2:31 
GeneralCrashing when I want to drag and drop between two collections on the same viewmodel [modified] PinmemberDannyStaten22-Oct-10 17:28 
GeneralRe: Crashing when I want to drag and drop between two collections on the same viewmodel PinmemberDaemon44431-Oct-10 21:34 
GeneralRe: Crashing when I want to drag and drop between two collections on the same viewmodel PinmemberDannyStaten3-Nov-10 4:32 
QuestionAwesome... how do I keep my double click event from getting lost though? PinmemberDannyStaten12-Oct-10 12:09 
GeneralRe: Awesome... how do I keep my double click event from getting lost though? [modified] PinmemberDannyStaten12-Oct-10 12:31 
GeneralRe: Awesome... how do I keep my double click event from getting lost though? PinmemberSteven Kirk12-Oct-10 21:57 
GeneralRe: Awesome... how do I keep my double click event from getting lost though? Pinmemberthegrid.ch31-Aug-11 21:19 
Generalanimating while dragging PinmemberMember 459998124-Sep-10 10:00 
GeneralRe: animating while dragging PinmemberSteven Kirk24-Sep-10 15:05 
GeneralRe: animating while dragging PinmemberMember 459998124-Sep-10 16:58 
Generalneed the feature of editing Pinmemberhjy121016-Apr-10 17:35 
Questionsetting IsDragSource only on certain nodes of a TreeView PinmemberGuy Shtub17-Mar-10 7:13 
AnswerRe: setting IsDragSource only on certain nodes of a TreeView PinmemberSteven Kirk18-Mar-10 2:52 
GeneralRe: setting IsDragSource only on certain nodes of a TreeView PinmemberGuy Shtub20-Mar-10 22:58 
Generalusing the Visual as feedback PinmemberGuy Shtub16-Mar-10 6:11 
GeneralRe: using the Visual as feedback PinmemberSteven Kirk18-Mar-10 2:55 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140902.1 | Last Updated 7 Nov 2009
Article Copyright 2009 by Steven Kirk
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid