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

Drag and Drop in WPF - Part II

By , 11 Nov 2009
 

Introduction

The is the second article on adding drag and drop to WPF applications using the GongSolutions.Wpf.DragDrop library. You can find the first part here: Drag and Drop in WPF Part I.

Background

In the first article, I described how to add simple drag and drop behaviour to an ItemsControl using attached properties, and showed how drop behaviour could be customised by adding a drop handler. If you've not seen that article, I'd recommend reading it first.

Using the Code

In this article, I'll explain how to customise drag behaviour with drag handlers, how to display a drag adorner, and how to handle multiple selections.

Drag Handlers

In the same way that drop handlers allow us to write code to customise the handling of a drop on a control, drag handlers allow us to customise the drag itself. To demonstrate this, I'm going to use the example we constructed in part 1: the Schools example.

In our application, let's suppose that there are certain pupils who don't want to be moved out of their school. For the sake of this example, I'm going to hardcode a pupil by his name - "Tom Jefferson". In a real-life application, you'd obviously not be hard-coding information like this, but for the sake of keeping the example simple, that's exactly what I'm going to do!

To prevent Tom Jefferson being moved, we first need to add a DragHandler to the pupils ListBox:

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

Here, like the DropHandler, we're binding the drag handler to the MainViewModel. To make MainViewModel a drag handler, we need to implement the IDragSource interface:

void IDragSource.StartDrag(DragInfo dragInfo)
{
    PupilViewModel pupil = (PupilViewModel)dragInfo.SourceItem;

    if (pupil.FullName != "Tom Jefferson")
    {
        dragInfo.Effects = DragDropEffects.Copy | DragDropEffects.Move;
        dragInfo.Data = pupil;
    }
}

The IDragSource interface only defines a single method, StartDrag. In our implementation, we first check that the pupil is not named "Tom Jefferson", and if not, we tell the framework that both Copy and Move operations are allowed. Next, we set the drag data - both the Effects and Data properties must be set in a drag handler to allow a drag to start.

Run your program, and there it is: Tom Jefferson is no longer draggable.

Displaying a Drag Adorner

A drag adorner is a transparent image that shows a preview of the data being dragged. When we drag a pupil, we're going to display a drag adorner that looks like this:

gong-wpf-dragdrop-ii1.png

Apologies for my lack of artistic skill on the icon, but you should get the idea. Feel free to use a better icon of your own :)

To show the adorner, first, we create a DataTemplate. We'll place it in the Window's Resources section:

<Window.Resources>
    <DataTemplate x:Key="PupilDragAdorner">
        <StackPanel>
            <Image Source="/Person.png" Width="64" 
                   HorizontalAlignment="Center"/>
            <TextBlock Text="{Binding FullName}" HorizontalAlignment="Center"/>
        </StackPanel>
    </DataTemplate>
</Window.Resources>

This DataTemplate consists of a StackPanel which vertically arranges two controls: an Image holding the icon and a TextBlock to display the pupil's name.

Now, we just need to set the DragAdornerTemplate attached property on the our ListBox:

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

And Hey Presto! The adorner appears when you drag an item from the pupils ListBox.

Multiple Selections

If multiple selection is available on the source ItemsControl, then the default drag handler will handle this. However, when more than one item of data is dropped, you'll no longer receive a single object, but a collection of objects. The simplest way to deal with this in your drop handler is to check whether the dragged data is either an object of the accepted type or an IEnumerable of the accepted type. So, the DragOver method in the example method would need to change to this:

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

Here we're checking to see if the data is a PupilViewModel or an IEnumerable<PupilViewModel>.

You'll also need to handle the possibility of a multiple selection in your IDropTarget.Drop method, but I'll leave that as an exercise to the reader.

Similarly, to enable an IDragSource to handle multiple selections, you need to look at the DragInfo.SourceItems property rather than the DragInfo.SourceItem property in your StartDrag method.

Points of Interest

Dragging a multiple selection is currently slightly broken. In WPF, if a user has made a multiple selection on an ItemsControl and then clicks on one of these items to start a drag, the multiple selection is cleared! So the framework checks to see if this is the case, and if so, swallows the click so that the multiple selection is not lost. However, this has the side-effect that if all items in the control are selected, there's nowhere for them to click to remove the multiple selection! Any hints on how to solve this problem will be gratefully received.

License

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

About the Author

Steven Kirk

United Kingdom United Kingdom
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionSlider in ListboxItem does not workmemberJMHOOK21-Oct-12 23:55 
GeneralMy vote of 5memberJecho Jekov30-Sep-12 19:43 
QuestionWindows Explorer integrationmemberlill.LEIF1-Sep-12 12:35 
AnswerRe: Windows Explorer integrationmemberSteven Kirk3-Sep-12 8:13 
GeneralRe: Windows Explorer integrationmemberlill.LEIF5-Sep-12 12:48 
Correct, I just didn't figure out myself that the object was of type IDropData. Sigh | :sigh: In case anybody else needs it, here's the code I used;
void IDropTarget.DragOver(IDropInfo dropInfo)
{
    IDataObject dataObject = (dropInfo.Data as IDataObject);
    if (dropInfo.DragInfo != null)
    {
       GongSolutions.Wpf.DragDrop.DragDrop.DefaultDropHandler.DragOver(dropInfo);
    }
    else if (dataObject != null && dataObject.GetDataPresent(DataFormats.FileDrop, true))
    {
       string[] filenames = (string[])dataObject.GetData(DataFormats.FileDrop, true);
...
... and of couse the same way in IDropTarget.DragDrop.
 
Problem solved! Thanks!
QuestionStill dragging when a TextBox is selectedmemberBragolgirith9-Aug-12 7:00 
AnswerRe: Still dragging when a TextBox is selectedmemberbalazs414-Jan-13 5:11 
QuestionDoesn't workmembercaioshin16-Jul-12 10:53 
BugYou did not include the right source codememberClifford Nelson9-May-12 11:03 
GeneralRe: You did not include the right source codemembersbarne32-Jul-12 11:55 
GeneralRe: You did not include the right source codememberVitor Barbosa6-Nov-12 8:12 
Suggestiondecouple ViewModel and View a bit morememberbitbonk12-Apr-12 21:17 
Questiondrag and drop between usercontrolsmembermichaelgr112-Mar-12 5:42 
Questiondifferent viewsmembermichaelgr19-Mar-12 5:37 
QuestionMultiple drop templatesmemberQaiser_Iftikhar27-Oct-11 22:59 
QuestionDragInfo null inside IDropTarget.Dropmemberscomcleod18-Jul-11 5:15 
AnswerRe: DragInfo null inside IDropTarget.Dropmemberscomcleod18-Jul-11 5:31 
GeneralRe: DragInfo null inside IDropTarget.Dropmembersbarne32-Jul-12 11:58 
GeneralRe: DragInfo null inside IDropTarget.Dropmemberscomcleod3-Jul-12 4:01 
QuestionDrop happening TWICE!membercbordeman23-Jun-11 10:00 
GeneralAbout DragInfo.DragStartPosition - e.GetPosition(null) is misleading when using with Context menumemberVermaManish26-Apr-11 20:42 
GeneralInterface extended To CancelDrag()memberDrNimnull1-Apr-11 9:22 
GeneralGet Source ParentmemberShady14u14-Mar-11 6:48 
GeneralMy vote of 5memberkmorris14-Jan-11 5:52 
QuestionUsing with c# usercontrol hosted in winformHostmembermach223-Jan-11 8:54 
GeneralMy vote of 5memberko001u28-Dec-10 23:22 
GeneralAdded the ability to delay auto expand behavior so you hover over something to open itmemberDannyStaten16-Oct-10 11:02 
GeneralRe: Added the ability to delay auto expand behavior so you hover over something to open itmemberSteven Kirk18-Oct-10 3:02 
GeneralMy vote of 5memberDannyStaten14-Oct-10 6:40 
QuestionAuto Scrolling?memberDannyStaten13-Oct-10 19:24 
AnswerRe: Auto Scrolling?memberDannyStaten15-Oct-10 17:07 
GeneralQuestion for use on a treeview? - how to make child levels drag and droppablememberDannyStaten12-Oct-10 18:57 
GeneralRe: Question for use on a treeview? - how to make child levels drag and droppablememberSteven Kirk12-Oct-10 22:03 
GeneralRe: Question for use on a treeview? - how to make child levels drag and droppable [modified]memberDannyStaten13-Oct-10 4:42 
GeneralRe: Question for use on a treeview? - how to make child levels drag and droppablememberDannyStaten13-Oct-10 19:31 
GeneralBest solution for general MVVM works.memberzokbari9-Aug-10 18:05 
GeneralDrag n Drop from listbox to Custom ControlmemberYer29-Jun-10 7:05 
QuestionRe: Drag n Drop from listbox to Custom ControlmemberThomas_M15-Aug-11 2:58 
GeneralMultiSelect IssuememberAnthony Osicka16-Jan-10 14:44 
GeneralRe: MultiSelect IssuememberMember 78731031-Mar-12 0:12 
GeneralRe: MultiSelect IssuememberVitor Barbosa13-Nov-12 7:10 
GeneralA messagebox at Listbox previewkeydown...dragging occurs from messgae location [modified]memberbspro11-Jan-10 3:33 
QuestionQuestion on use with treeviewmemberpiotri8522-Dec-09 10:28 
AnswerRe: Question on use with treeviewmemberSteven Kirk23-Dec-09 0:51 
GeneralCannot Drag to a listbox with group stylememberbspro3-Dec-09 5:17 
GeneralRe: Cannot Drag to a listbox with group stylememberSteven Kirk7-Dec-09 9:55 
GeneralRe: Cannot Drag to a listbox with group stylememberSteven Kirk10-Dec-09 1:49 
GeneralRe: Cannot Drag to a listbox with group stylememberbspro15-Dec-09 2:44 
GeneralRe: Cannot Drag to a listbox with group stylememberSteven Kirk15-Dec-09 9:11 
GeneralRe: Cannot Drag to a listbox with group stylememberPrasad Thammineni12-Feb-10 11:18 

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

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130617.1 | Last Updated 11 Nov 2009
Article Copyright 2009 by Steven Kirk
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid