Ask Learn
Preview
Ask Learn is an AI assistant that can answer questions, clarify concepts, and define terms using trusted Microsoft documentation.
Please sign in to use Ask Learn.
Sign inThis browser is no longer supported.
Upgrade to Microsoft Edge to take advantage of the latest features, security updates, and technical support.
Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
How to do Drag& Drop in WPF is a question I hear often...
I have seen some great samples out there, but most focus on either a big code sample or a niche scenario... a couple of times I have ended up having to help some one who got stuck.
I hope the below write up is useful to explain the steps and decisions to get drag & drop done.. and it comes with sample snippets ...
-- ---- -------------------
From [https://msdn2.microsoft.com/en-us/library/aa289508(vs.71).aspx] Here is the sequence of events in a typical drag-and-drop operation:
Dragging is initiated by calling the DoDragDrop method for the source control.
The DoDragDrop method takes two parameters:
A new DataObject object is automatically created.
This in turn raises the GiveFeedback event. In most cases you do not need to worry about the GiveFeedback event, but if you wanted to display a custom mouse pointer during the drag, this is where you would add your code.
Any control with its AllowDrop property set to True is a potential drop target. The AllowDrop property can be set in the Properties window at design time, or programmatically in the Form_Load event.
As the mouse passes over each control, the DragEnter event for that control is raised. The GetDataPresent method is used to make sure that the format of the data is appropriate to the target control, and the Effect property is used to display the appropriate mouse pointer.
If the user releases the mouse button over a valid drop target, the DragDrop event is raised. Code in the DragDrop event handler extracts the data from the DataObject object and displays it in the target control.
------------
Before the DoDragDrop is called, we must detect a mouse Drag operation on the source... A mouse drag is usually a MouseLeftButtonDown + a MouseMove (before MouseLeftButton goes up) ...
So, our drag & drop source control needs to subscribe to these two events:
void Window1_Loaded(object sender, RoutedEventArgs e) { this.DragSource.PreviewMouseLeftButtonDown += new MouseButtonEventHandler(DragSource_PreviewMouseLeftButtonDown); this.DragSource.PreviewMouseMove += new MouseEventHandler(DragSource_PreviewMouseMove); } |
To prevent from starting a false drag & drop operation where the user accidentally drags, you can use SystemParameters.MinimumHorizontalDragDistance and SystemParameters.MinimumVerticalDragDistance
One way to do this is on MouseLeftButtonDown, record the starting position and onMouseMove check if the mouse has moved far enough..
The data! You need to find out what is under the mouse when dragging.
I will omit take the easy way out and assume that whoever is triggering the MouseMove is what I want to drag .. so look at MouseEventArgs.OriginalSource.. [or you could do some 2D HitTesting using VisualTreeHelper .. In Part3 of this write up will try to walk you through hit testing the listbox -which is the other common scenario I encounter-.
Once you have the object to drag, you will need to package what you are a sending into a DataObject that describes the data you are passing around.
DataObject is a wrapper to push generic data (identified with extensible formats) into drag/drop.. As long as both the source and destination understand the format, you will be set. As such, DataObject has a couple interesting methods:
Not much interesting stuff here.. In the sample I just hard-coded my data to be of type string... this makes it easier to paste into external containers (for example Word, which you can use to test this part of the write-up). I do have to stress that drag & dropping should be about the data ...
Before we call DoDragDrop () we have a few 'choices' to make around the feedback we want to provide and the 'scope' of the d&d.
If you don't want a fancy cursor, you are done!! You can call DoDragDrop directly ...
Note: this code allows you to drag & drop across processes, it uses the default operating system feedback ( e.g. + for copy)..
Say we had a .cur file and embedded it on to our application as a resource ( see sample code). We can subscribe to GiveFeedback () and wire our cursor there..
Our handler for feedback looks like this:
Two things to notice:
1) I cached the cursor... GiveFeedback will be called many times as the mousemoves so I cached it.. and
2) though I did not handle it, I called it "_allOpsCursor" because GiveFeedbackEventArgs will tell you the possible operation for the cursor (e.Effects)... I could have used multiple cursors, one for each effect.
The first thing you will need is to an Adorner; in my case I chose and adorner that contains a VisualBrush of the Element being dragged... you can go with RenderTargetBitmap, or possibly reparent the object directly ... but I like VisualBrush in case the drag is cancelled..
The constructor for the adorner class is where most of the action happens:
//There is more code in DragAdorner, but mostly used for positioning the adorner as the drag is happening... please refer to the sample...
Now, that we have our custom adorner ready, the tricky part is wiring it so it follows the cursor position. There are two options here:
First we have to import Win32's code using DllImport .... [trivial stuff, refer to sample code in Win32.cs ]
Next we create an instance of a Window, which will contain a visual brush of the element we are dragging ...
Notice:
1) I set the style to Transparent, layered window (this is ok since the window is small and it is only used for drag & drop ).. and
2) the call to UpdateWindowLocation () this is the code that positions the Window wherever the cursor is now..
3) I likely need more error checking
The code in UpdateWindowLocation is straight forward:
This UpdateLocation code of course needs to be called whenever the cursor moves... so we need some kind of callback during the drag operation.. We will use QueryContinueDrag for that..
So, I go back to the code in StartDrag () and wire up the event, as well as some code to show the window and destroy it after drag & drop:
|
The one thing to notice is that I still have GiveFeedbackHandler wired.. Why ?? We are no longer using the cursor... but we still have to tell Drag & Drop not to use the default cursors..
There is a slightly different approach you can use if you are drag & dropping just inside your app or have a smaller scope ... I some times use this approach because it allows me to avoid interop, avoid creating extra windows, and better control the scope of the drag...
Here is the full explanation of how it works and why it feels like hackalicious.
When you call DoDragDrop, there is no Mouse or Cursor Events being fired in your WPF app... OLE does the work for you and it moves cursor directly :(... however, all of the Drag events are being fired...
We already know of the two events we can tap into from the source: GiveFeedback and QueryContinueDrag... however neither of these events gives us access to the mouse or cursor position during the drag operation :( ... We can however tap into the Dragover event; DragOverEventArgs has a GetPosition ( ) method that does the trick... DragOver however is fired in the target, not the source.
So, how would we do it?? Well , DragEvents are routed events.. they bubble up.. if we define a "Drag Scope" within our app that we know is guaranteed to bubble the DragOver, then we can listen for it ... the obvious choice for that scope is our Application's Window; this gives us access to any thing in our app; the scope could be smaller of course...
Here is how we wire that:
Explanations:
There is a common gotcha with the DragLeave part of this scenario. The scope tends to always be a panel,grid, etc.. ( a container) and if the container has background to null, it is not hittesting, so you won't get the dragleave... You have to explicitly set the Background="Transparent" to make sure you get it... (you can see it in my sample with the Grid)..
That is it for Drag ... I hope I explained how to do the Drag part of a drag & drop. I want to cut part 1 here so that you have a pretty clean sample of the "drag" ..
The source for every thing above is here.
You will have to tweak the MouseMove function to select which drag approach to use.. Just make sure you have at most one of these functions uncommented at any time..
// StartDrag(e);
// StartDragCustomCursor(e);
// StartDragWindow(e);
StartDragInProcAdorner(e);
Since I did not wire a Drop handler, for testing this, just "Drop" into some thing that handles Text like Microsoft Word..
In part 2, I will cover the drop .. and in part 3 I will share the complete code with a couple of extra things that I omitted here to try to keep it clean (some of them might be in the code sample)
Anonymous
July 12, 2007
Cookieless Session with ASP.NET Ajax and Web Services [Via: derek ] Displaying Extended Details in a...
Anonymous
July 14, 2007
Great stuff. I found the Using Visual for Feedback.1 : D&D across apps using GetCursorPos() section helpful. To achieve the same goal as you did with your helper classes, I've created custom controls that wrap others and encapsulate the drag and drop functionality so you can implement it declaratively. If you're interested I can send you the code and a sample -- maybe you could give me some feedback.
Anonymous
July 19, 2007
Wow, that's really a complete explanation. I wish I'd had that back then :) But we had some code... :)
On the D&D across apps... would you think it could work with Popup-Controls? That might help get around interop... not sure if there's a caveat or not.
cheers
Florian
Anonymous
July 29, 2007
Dr Tim Sneath gives a overview of what is new in WPF 3.5 http://blogs.msdn.com/tims/archive/2007/07/27
Anonymous
July 29, 2007
Apologies for belated reply.. I was on vacation..
Florian, I have not tried Popup; technically it is same than window, in terms of it requiring FullTrust..
Another possible disadvantage of Popup is the slightly less control you will have when creating the window (or Popup) itself.. I doubt you can set the right style.. you could doing interop(SetWindowx) of course..
Let me know how it goes if you try Popup.
Anonymous
October 15, 2008
本文讲述了在一个ItemsControl内部,ListView内部,以及两者之间互相拖动item的实现。涉及到了DataTemplate、Style、Adorner、ContentPresenter、...
Ask Learn is an AI assistant that can answer questions, clarify concepts, and define terms using trusted Microsoft documentation.
Please sign in to use Ask Learn.
Sign in