Click here to Skip to main content
15,867,594 members
Articles / Desktop Programming / WPF

Drag and Drop in WPF

Rate me:
Please Sign up or sign in to vote.
4.80/5 (44 votes)
7 Nov 2009BSD5 min read 322.4K   21.8K   100   63
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:

XML
<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:

XML
<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:

XML
<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:

C#
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:

C#
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:

C#
((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


Written By
United Kingdom United Kingdom
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionMessage Closed Pin
9-Nov-21 16:21
ahello9-Nov-21 16:21 
QuestionGetting an error Pin
Member 123817669-Mar-16 20:12
Member 123817669-Mar-16 20:12 
AnswerRe: Getting an error Pin
Robert7705128-Apr-16 11:01
professionalRobert7705128-Apr-16 11:01 
PraiseVery great lib Pin
tuxy4221-Nov-15 14:50
tuxy4221-Nov-15 14:50 
Questiongong doesnt work !! Pin
Member 112829847-Dec-14 19:39
Member 112829847-Dec-14 19:39 
BugRe-ordering items doesn't seem to work. Pin
Jan Böhm29-Sep-14 7:32
Jan Böhm29-Sep-14 7:32 
SuggestionRe: Re-ordering items doesn't seem to work. Pin
Member 1227325127-Jan-16 3:20
Member 1227325127-Jan-16 3:20 
GeneralAwesome control! Pin
JonHarder29-Jul-14 14:47
JonHarder29-Jul-14 14:47 
QuestionCan drop onto canvas but can't figure out how to pick the dropped item up again Pin
Member 1066774913-Mar-14 5:27
Member 1066774913-Mar-14 5:27 
QuestionProblem with dragging from outside of application Pin
Saadh6666-Feb-14 3:19
Saadh6666-Feb-14 3:19 
GeneralGreat! Pin
Hamed Musavi24-Dec-13 9:37
Hamed Musavi24-Dec-13 9:37 
GeneralMy vote of 4 Pin
shashank7685-Sep-13 23:59
shashank7685-Sep-13 23:59 
GeneralMy vote of 5 Pin
Kim Togo29-Aug-13 3:10
professionalKim Togo29-Aug-13 3:10 
QuestionDragging and dropping to and from a usercontrol and an itemscontrol Pin
LooLooClement17-Aug-13 4:09
LooLooClement17-Aug-13 4:09 
GeneralA thing of beauty Pin
Dijj7-Jun-13 23:19
Dijj7-Jun-13 23:19 
Questioncan't unselect item from mutiple selected items in ListBox Pin
srikanth.chippa5-Mar-13 0:55
srikanth.chippa5-Mar-13 0:55 
QuestionIssue with TabControl Pin
Coxianuk27-Feb-13 6:59
Coxianuk27-Feb-13 6:59 
GeneralMy vote of 5 Pin
Wayne Gaylard4-Jan-13 0:45
professionalWayne Gaylard4-Jan-13 0:45 
GeneralMy vote of 5 Pin
Jecho Jekov30-Sep-12 19:43
Jecho Jekov30-Sep-12 19:43 
Questiondon't work correctly Pin
michaelgr130-Jun-12 21:02
michaelgr130-Jun-12 21:02 
Questiondragging to a child in a treeview Pin
boogster71427-Mar-12 1:05
boogster71427-Mar-12 1:05 
QuestionCan't Get It To Work Pin
Kevin Marois1-Mar-12 12:29
professionalKevin Marois1-Mar-12 12:29 
GeneralMy vote of 5 Pin
Member 867759026-Feb-12 3:14
Member 867759026-Feb-12 3:14 
QuestionExcellent! Pin
caliban2621-Jan-12 0:37
caliban2621-Jan-12 0:37 
QuestionGREAT! Pin
Member 84445886-Jan-12 9:53
Member 84445886-Jan-12 9:53 
Wonderful bit of code. I was wondering if there was a trigger for when I do a reordering of the slides?

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

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