In my previous post on this topic, I gave a quick overview of how Drag and Drop can be accomplished using attached properties. To review, here are the key elements of the implementation:
- DragDropManager - top level class that exposes the attached properties (DragSourceAdvisor, DropTargetAdvisor); does all the DnD plumbing and fires methods on the IDragSourceAdvisor and IDropTargetAdvisor interfaces
- IDragSourceAdvisor - implemented by the source control and advises the DragDropManager on how the source behaves during DnD
- IDropTargetAdvisor - implemented by the target control and advises the DragDropManager on how the target behaves during DnD
The property changed handlers for the attached properties hooks up events for the source and target controls. Below is the handler for the DragSourceAdvisor property. Note that here I am hooking up to the Mouse events for initiating DragDrop. These events are primarily used to detect the drag gesture. For more information read Marcelo’s blog.
private static void OnDragSourceAdvisorChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs args) { UIElement sourceElt = depObj as UIElement; if (args.NewValue != null && args.OldValue == null) { sourceElt.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(DragSource_PreviewMouseLeftButtonDown); sourceElt.PreviewMouseMove += new MouseEventHandler(DragSource_PreviewMouseMove); sourceElt.PreviewMouseLeftButtonUp += new MouseButtonEventHandler(DragSource_PreviewMouseLeftButtonUp); sourceElt.PreviewGiveFeedback += new GiveFeedbackEventHandler(DragSource_PreviewGiveFeedback); // Set the Drag source UI IDragSourceAdvisor advisor = args.NewValue as IDragSourceAdvisor; advisor.SourceUI = sourceElt; } else if (args.NewValue == null && args.OldValue != null) { sourceElt.PreviewMouseLeftButtonDown -= DragSource_PreviewMouseLeftButtonDown; sourceElt.PreviewMouseMove -= DragSource_PreviewMouseMove; sourceElt.PreviewMouseLeftButtonUp -= DragSource_PreviewMouseLeftButtonUp; sourceElt.PreviewGiveFeedback -= DragSource_PreviewGiveFeedback; } }
and the DropTargetAdvisor property handler:
private static void OnDropTargetAdvisorChanged(DependencyObject depObj, DependencyPropertyChangedEventArgs args) { UIElement targetElt = depObj as UIElement; if (args.NewValue != null && args.OldValue == null) { targetElt.PreviewDragEnter += new DragEventHandler(DropTarget_PreviewDragEnter); targetElt.PreviewDragOver += new DragEventHandler(DropTarget_PreviewDragOver); targetElt.PreviewDragLeave += new DragEventHandler(DropTarget_PreviewDragLeave); targetElt.PreviewDrop += new DragEventHandler(DropTarget_PreviewDrop); targetElt.AllowDrop = true; // Set the Drag source UI IDropTargetAdvisor advisor = args.NewValue as IDropTargetAdvisor; advisor.TargetUI = targetElt; } else if (args.NewValue == null && args.OldValue != null) { targetElt.PreviewDragEnter -= DropTarget_PreviewDragEnter; targetElt.PreviewDragOver -= DropTarget_PreviewDragOver; targetElt.PreviewDragLeave -= DropTarget_PreviewDragLeave; targetElt.PreviewDrop -= DropTarget_PreviewDrop; targetElt.AllowDrop = false; } }
Since all the event handlers are part of the DragDropManager, all the dirty work
will be done here and the appropriate interface methods will be called. The calls to these interface methods are spread across the different event handlers.
Examples
In the attached zip, I have included two examples:
- Three panels where you can drag and drop buttons between these panels
- DragCanvas example where the source and target is the same. Apparently while writing this post, I realized that the DragCanvas example is similar to one that Josh Smith posted sometime back. However my example has far less code since all the DnD logic is hidden away
The source for both the examples should be pretty self-explanatory so I am not going over it. Since both the examples are part of the same project, you will have to change the StartupUri in App.xaml to switch between examples. Use the following StartupUris:
Anyways, feel free to use the code in your own apps. If you do come up with a scenario where the interface methods are not sufficient, say, drop me a line and I would be glad to fix it. Any feedback…good, bad, ugly is welcome

By Model-object I meant the instance of your Data model. If you can define a DataTemplate for it, you could create a ContentPresenter, set the Content to your data-model and the ContentTemplate to your DataTemplate. Then using the VisualBrush of the ContentPresenter you could render the graphics for the DnD hover. HTH.
Yes, I am aware of the problem with the use of x:Name in UserControls. I think its an issue with the XamlReader class and more specifically the Xaml Parser. On a similar note I have also encountered issues using x:Name for elements inside a ResourceDictionary.
The other approach I can suggest is to pass on Model objects via DnD. Since I already provide you the visual brush on the Source side you should be able to see the actual element being moved. When hovering over the Target, instead of using the Serialized Xaml, you could recreate the visual using Model objects + DataTemplates. Let me know if that sounds feasible for your scenario.
Thanks!
Hi Riccardo,
I am currently in the process of tweaking my solution (with the feedback that I’ve received from the comments). I’ll put up the new source soon.
As regards your case, having the advisors for the Listboxes is the right approach. Having the advisor for the Window itself seems complicated, as you may have to do lots of checking to see if you are dragging the right element.
Yes, I was planning to add that for v2. The only thing is that you won’t get a preview feedback for cross-app DnD, but will be using the OS-specific version of the DnD cursor.
The oven has been turned on for baking
hey pavan
it is meant for cross app and thats why i need the draggedelt to be updated. could you pls update the library.
Quite possible
Yes, I don’t support cross-application support, not yet.
When you say its not working, is it the Drag preview that is failing? I feel that modifying the _draggedElt is the cause of the problems. Is it possible to move the code to DropTarget_PreviewDrop instead of using it inside the PreviewDragEnter?
You should not be modifying the DragDropManager for this kind of behavior. This code should be sitting inside the class that implements IDragSourceAdvisor. You also seem to be modifying the private variable _draggedElt, which is used at lot of other places inside the DragDropManager.
string serializedObject = e.Data.GetData("CanvasExample") as string;
System.Xml.XmlReader reader = System.Xml.XmlReader.Create(new System.IO.StringReader(serializedObject));
_draggedElt = System.Windows.Markup.XamlReader.Load(reader) as UIElement;
to the function as provided in your code. This should result in the same behavior. However, drag drop doesnt work now. If i remove these lines the code works fine. Just wanted to know whats happenning.
It is unclear to me as to what is not working. Could you give me more information about your scenario?
{
if (UpdateEffects(sender, e) == false) return;
string serializedObject = e.Data.GetData("CanvasExample") as string;
System.Xml.XmlReader reader = System.Xml.XmlReader.Create(new System.IO.StringReader(serializedObject));
_draggedElt = System.Windows.Markup.XamlReader.Load(reader) as UIElement;
CreatePreviewAdorner();
e.Handled = true;
}
Hello,
If I understand correctly, you want to change the drag start point ??
This is very cool stuff, I finally understood DependencyProperties because of your article/code.
One question: How can I make the drag start by leaving a small area around the drag start location instead of starting it by leaving the IDragSourceAdvisor element?
Thanks for this great article!
[...] ( Part 1, Part 2, Part 3, Part 4, Part 5, Part 6 [...]
Hi Pavan,
Great code. Really loving Fluid Kit.
I’m having one problem with the DragDropManager however. I’m using with a XamDataGrid from Infragistics with your code.
I’ve had to code around some of the built in features of the Grid in order to get the DragDrupManager to work nicely with this grid. However I have one problem that I can’t seem to solve. The grid allows grouping of records I’ve added in logic to make the GroupByArea expand and collapse as it would on a grid without the manager plugged in, but I can’t seem to get the individual groups to expand once the records have been grouped.
Do you have any ideas? Its like the DragDropManager is hijacking the events that would power the grids functionality. Ive tried conditionally marking the event as e.Handled = false but its still not allowing the normal grid events to function after evaluating if its a drag event or not.
Any ideas would be great.
James,
The DnD manager uses the PreviewMouse events to handle the DnD functionality. This is probably something that I’ll have to change in order to allow the app to process it. You could also check to see if there are other controls in the visual tree that may be intercepting the events. For example Button, ListBox are few controls that gobble up most of the Mouse events.
This realy simplifies the drag/drop process. However, your examples use XMAL code. I need to create new buttons and areas in C#. How do I set up a new advisor in C#? I’ve tried using Resource.Add, which it doesn’t complain about, but nothing happens.
Thanks.
Hi Pavan,
I am reading your book on WPF Control Development. I am getitng to learn so much from your book. I have just transitioned to WPF from ASP.Net and still learning. I have to implement Drag and Drop events on my Tree view control following the MVVM pattern. In your book, in the attached properties chapter you provided a solutiuon for Drag and Drop using Canvas, Is it possible for you ro provide a similar example for TreeView Control.
Thank you in advance.