How to update the position of a drag adorner during WPF drag-n-drop?

12,301

Solution 1

There's this (unfortunately only available as a cached version) pretty old blog post from Bea Stollnitz, which pretty much covers your question. It has a nice implementation of drag n drop with an adorner showing a "ghost image".

Basically drag and drop in WPF is quite the complicate procedure that - if you want some custom DragAdorners - involves adding a bunch of attached dependency properties for handling setup of all the events involved and especially for displaying the adorner in a way that doesn't interfere with the dropping code.

Bea's code works by having a helper class that sets the owning Window's DragOver event handler and AllowDrop right before the actual drag drop operation, that way you can control all the moving in between the actual drag source and the drop target.

Solution 2

So, looking closer at Bea's code that redoced was referring to...

I still set AllowDrop="true" on the top level grid and give it a DragOver handler where I can update the adorner position, but I also set the DragDropEffects to None here. Then I just need to add a DragOver handler to the actual drop target to also update the adorner position...and making sure to set e.Handled = true so that the top level grid's handler doesn't just set the effects back to None when I'm over a drop target...

private void TopLevelGrid_OnDragOver(object sender, DragEventArgs e)
{
 UpdateDragAdornerPosition(e.GetPosition(topLevelGrid));
 e.Effects = DragDropEffects.None;
 e.Handled = true;
}

private void DropTarget_OnDragOver(object sender, DragEventArgs e)
{
 UpdateDragAdornerPosition(e.GetPosition(topLevelGrid));
 e.Handled = true;
}

Solution 3

I know this is an old question, but I ended up asking the same thing recently and then had to answer it myself. I used hooks via p/invoke to get at the native window messages before they were consumed by the drag and drop operation. This let me track the mouse even during drag and drop and without having to set AllowDrop where I didn't want it.

For the full answer (including most of the code I used) you can check out my question:
WPF - Track mouse during Drag & Drop while AllowDrop = False

Share:
12,301
IanR
Author by

IanR

I am currently working as a Software Developer at Nonlinear Dynamics Limited, a developer of proteomics and metabolomics software. My day job mainly involves developing Windows desktop applications with C# .NET. My hobby/spare-time development therefore tends to focus on playing around with some different technologies (which, at the minute seems to be web application development with JavaScript).

Updated on June 04, 2022

Comments

  • IanR
    IanR almost 2 years

    I'm using an adorner to show a 'ghost' of the element being dragged...

    var adornerLayer = AdornerLayer.GetAdornerLayer(topLevelGrid);
    dragAdorner = new DragAdorner(topLevelGrid, itemToDrag);
    adornerLayer.Add(dragAdorner);
    dragAdorner.UpdatePosition(e.GetPosition(topLevelGrid));
    
    DragDrop.DoDragDrop(sourceItems, viewModel, DragDropEffects.Move);
    
    adornerLayer.Remove(dragAdorner);
    itemToDrag = null;
    

    ...but I can't find a nice way to update the position of the adorner during the drag. The closest I've got is by setting AllowDrop="true" on the top level grid and giving it a DragOver handler...

    private void TopLevelGrid_OnDragOver(object sender, DragEventArgs e)
    {
     dragAdorner.UpdatePosition(e.GetPosition(topLevelGrid));
    }
    

    But this means I don't get the proper DragDropEffects feedback on the cursor, i.e., it always shows the DragDropEffects.Move cursor instead of DragDropEffects.None until I'm over an actual drop target.

    Does anyone know a better way to update the adorner position?