Site Logo

Pixel-in-Gene

Exploring Frontend Development with Design / Graphics / Technology

Drag & Drop with attached properties - Part 2

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:

CanvasExample: StartupUri="CanvasExample/DragCanvasExample.xaml"

PanelExample: StartupUri="PanelExample/Window1.xaml"

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 ;)

Download Zip

Drag & Drop with attached properties - Part 2
Pavan Podila
Pavan Podila
November 20th, 2006