Like pancakes...

the random ramblings begin...
in

Generic WPF Drag and Drop Adorner

I was recently working with a WPF application where we wanted drag and drop functionality. We were going to have multiple types of draggable items and different drop locations, and being the conscientious programmer that I am, I wanted to make something as generic as possible. I started from Bea Stollnitz’s post about drag and drop between ItemsControls and went from there.

All the source is available with the usual disclaimer that it comes “as-is” without any express or implied warrantee or support.

Concept

There are three main pieces for any drag and drop:

  • Draggable Control – simply, the UIElement you’d like the user to be able to mouse-down and drag
  • Drag Adorner – the control you are dragging around
  • Droppable Area – a target area where the mouse-up (drop) will cause an action

I’ve made two base classes to handle the creation and definition of these three pieces and their intrinsic functionality. Note: these use some object oriented programming and WPF concepts (e.g. inheritance, dependency properties, attached properties) and if you’re not familiar with them you might want to do some quick background reading before continuing.

Base Functionality

The drag and drop functionality has to include some style, even if the controls themselves don’t (I’m just a humble programmer, folks, not a designer). So, on top of basic dragging and dropping I’ve added the following touches.

First, when you drag the object we are going to create a different view of the object to move around (the Drag Adorner). This is especially useful when the Draggable Control is large or contains a lot of text.

Second, we want the user to be conscious of the effect of their drop. So, we’ll use two animations: one to accept the object when dropped over the Droppable Area, and one to suck it back to the start point when dropped outside of the Droppable Area.

DragDropAdornerBase.cs

This is a class that inherits from UserControl without a .xaml file - allowing us to inherit our own Drag Adorner user control from it. The highlights of this file are:

  • DropState – a simple enumeration where:
    • CanDrop means the Drag Adorner is inside the Droppable Area
    • CannotDrop means the Drag Adorner outside the Droppable Area
public enum DropState
{
    CanDrop = 1,
    CannotDrop = 2
}
  • AdornerDropState – a dependency property to track the DropState of the object
public DropState AdornerDropState
{
    get { return (DropState)GetValue(AdornerDropStateProperty); }
    set { SetValue(AdornerDropStateProperty, value); }
}

public static readonly DependencyProperty AdornerDropStateProperty =
    DependencyProperty.Register("AdornerDropState", typeof(DropState),
    typeof(DragDropAdornerBase), new UIPropertyMetadata(DropStateChanged));
  • StateChangedHandler – a virtual method called from the property changed callback to handle any state specific UI changes. This is used in one of the example implementations below.
public static void DropStateChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    DragDropAdornerBase myclass = (DragDropAdornerBase)d;
    myclass.StateChangedHandler(d,e);
}

public virtual void StateChangedHandler(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
}

DragDropHelper.cs

This class defines the attached properties used to declare the Draggable Control, Drag Adorner and Droppable Area and registers listeners to control the functionality of the drag and drop.

  • IsDragSource – a simple boolean which, set to True, defines a Draggable Control. The property changed callback, IsDragSourceChanged, wires up the required event listeners
public static readonly DependencyProperty IsDragSourceProperty =
    DependencyProperty.RegisterAttached("IsDragSource", typeof(bool),
    typeof(DragDropHelper), new UIPropertyMetadata(false, IsDragSourceChanged));
private static void IsDragSourceChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
{
    var dragSource = obj as UIElement;
    if (dragSource != null)
    {
        if (Object.Equals(e.NewValue, true))
        {
            dragSource.PreviewMouseLeftButtonDown += Instance.DragSource_PreviewMouseLeftButtonDown;
            dragSource.PreviewMouseLeftButtonUp += Instance.DragSource_PreviewMouseLeftButtonUp;
            dragSource.PreviewMouseMove += Instance.DragSource_PreviewMouseMove;
        }
        else
        {
            dragSource.PreviewMouseLeftButtonDown -= Instance.DragSource_PreviewMouseLeftButtonDown;
            dragSource.PreviewMouseLeftButtonUp -= Instance.DragSource_PreviewMouseLeftButtonUp;
            dragSource.PreviewMouseMove -= Instance.DragSource_PreviewMouseMove;
        }
    }
}
  • DragDropControl – a UIElement that defines the Drag Adorner control
  • AdornerLayer – a Canvas object for containing and displaying the Drag Adorner control
public static readonly DependencyProperty DragDropControlProperty =
    DependencyProperty.RegisterAttached("DragDropControl", typeof(UIElement),
    typeof(DragDropHelper), new UIPropertyMetadata(null));

public static readonly DependencyProperty AdornerLayerProperty =
    DependencyProperty.RegisterAttached("AdornerLayer", typeof(string),
    typeof(DragDropHelper), new UIPropertyMetadata(null));
  • DropTarget – the string name of the UIElement that defines the Droppable Area
public static readonly DependencyProperty DropTargetProperty =
    DependencyProperty.RegisterAttached("DropTarget", typeof(string),
    typeof(DragDropHelper), new UIPropertyMetadata(string.Empty));

Now that we’ve know how to define the necessary pieces, let’s look at how the event handlers define the functionality.

  • DragSource_PreviewMouseLeftButtonDown – sets DragDropHelper instance variables
  • DragSource_PreviewMouseLeftButtonUp – clears DragDropHelper instance variables. This method is only called if the mouse button is released prior to a recognized drag
  • DragSource_PreviewMouseMove – determines if the mouse move is large enough to be considered a drag. If a drag is detected this method shows the AdornerLayer Canvas, adds the defined Drag Adorner (DragDropControl) to the Canvas, and wires-up listeners to mouse move and mouse up actions on the Drag Adorner
private void DragSource_PreviewMouseMove(object sender, MouseEventArgs e)
{
    if (!_mouseCaptured && _draggedData != null)
    {
        // Only drag when user moved the mouse by a reasonable amount.
        if (DragDropHelper.IsMovementBigEnough(_initialMousePosition, e.GetPosition(_topWindow)))
        {
            _adorner = (DragDropAdornerBase)GetDragDropControl(sender as DependencyObject);
            _adorner.DataContext = _draggedData;
            _adorner.Opacity = 0.7;

            _adornerLayer.Visibility = Visibility.Visible;
            _adornerLayer.Children.Add(_adorner);
            _mouseCaptured = Mouse.Capture(_adorner);

            Canvas.SetLeft(_adorner, _initialMousePosition.X - 20);
            Canvas.SetTop(_adorner, _initialMousePosition.Y - 15);

            _adornerLayer.PreviewMouseMove+=new MouseEventHandler(_adorner_MouseMove);
            _adornerLayer.PreviewMouseUp += new MouseButtonEventHandler(_adorner_MouseUp);
        }
    }
}
  • _adorner_MouseMove – tracks the mouse movement during a drag to reposition the Drag Adorner on the AdornerLayer and updates the Drag Adorner’s AdornerDropState
private void _adorner_MouseMove(object sender, MouseEventArgs e)
{
    Point currentPoint = e.GetPosition(_topWindow);
    currentPoint.X = currentPoint.X - 20;
    currentPoint.Y = currentPoint.Y - 15;

    _delta = new Point(_initialMousePosition.X - currentPoint.X,
        _initialMousePosition.Y - currentPoint.Y);
    _scrollTarget = new Point(_initialMousePosition.X - _delta.X,
        _initialMousePosition.Y - _delta.Y);

    Canvas.SetLeft(_adorner, _scrollTarget.X);
    Canvas.SetTop(_adorner, _scrollTarget.Y);

    _adorner.AdornerDropState = DropState.CannotDrop;

    if (_dropTarget != null)
    {
        GeneralTransform t = _dropTarget.TransformToVisual(_adornerLayer);
        _dropBoundingBox = t.TransformBounds(
            new Rect(0, 0, _dropTarget.RenderSize.Width, _dropTarget.RenderSize.Height));

         if (e.GetPosition(_adornerLayer).X > _dropBoundingBox.Left &&
             e.GetPosition(_adornerLayer).X < _dropBoundingBox.Right &&
             e.GetPosition(_adornerLayer).Y > _dropBoundingBox.Top &&
             e.GetPosition(_adornerLayer).Y < _dropBoundingBox.Bottom)
        {
            _adorner.AdornerDropState = DropState.CanDrop;
        }
    }
}
  • _adorner_MouseUp – animates the Drag Adorner based on the AdornerDropState and cleans up the resources used for dragging. Note that this code assumes the Drag Adorner User Control defines two animations in it’s resources named “canDrop” and “cannotDrop”, and that further assumptions are made about the animation itself when the item is dropped outside the Droppable Area.
private void _adorner_MouseUp(object sender, MouseEventArgs e)
{
    switch (_adorner.AdornerDropState)
    {
        case DropState.CanDrop:
            try
            {
                ((Storyboard)_adorner.Resources["canDrop"]).Completed += (s, args) =>
                {
                    _adornerLayer.Children.Clear();
                    _adornerLayer.Visibility = Visibility.Collapsed;
                };
                ((Storyboard)_adorner.Resources["canDrop"]).Begin(_adorner);

                if (ItemDropped != null)
                    ItemDropped(_adorner, new DragDropEventArgs(_draggedData));
            }
            catch (Exception ex)
            { }
            break;
        case DropState.CannotDrop:
            try
            {
                Storyboard sb = _adorner.Resources["cannotDrop"] as Storyboard;
                DoubleAnimation aniX = sb.Children[0] as DoubleAnimation;
                aniX.To = _delta.X;
                DoubleAnimation aniY = sb.Children[1] as DoubleAnimation;
                aniY.To = _delta.Y;
                sb.Completed += (s, args) =>
                {
                    _adornerLayer.Children.Clear();
                    _adornerLayer.Visibility = Visibility.Collapsed;
                };
                sb.Begin(_adorner);
            }
            catch (Exception ex) { }
            break;
    }

Implementation

Now that the generic functionality is all set up we’re at the fun part: using it for specific implementations.

I’ve created two examples in a single window for these. The first example is dragging an image, and the second drags text. Both draggable controls have separate drop areas defined (in case you forgot, I’m not a designer).

image

Leveraging the generic code to create specific instances takes only a few short steps. The first step is creating a child class of the DragDropAdornerBase, defining the look and feel of the adorner. This should define appropriate animations as well as override the StateChangedHandler for DropState as necessary for specific UI changes.

Once the adorner control is defined, the attached properties have to be set on a UIElement to enable dragging and to define the adorner object, the adorner layer and the drop area.

Example1 – ImageDragDropAdorner

This example will enable us to drag and drop the album cover into the yellow drop area.

First, an override of the DragDropAdornerBase class called the ImageDragDropAdorner is created to define the look of the Drag Adorner. For this example we’ll simply show the album cover and the name of the album image.

<local:DragDropAdornerBase x:Class="Clarity.Demo.GenericDragDrop.Example1.ImageDragDropAdorner"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Clarity.Demo.GenericDragDrop"
    Height="100" Width="100">
    <UserControl.Resources>
        <Storyboard x:Key="canDrop"...>
        <Storyboard x:Key="cannotDrop"...>
        <Storyboard x:Key="loadAni"...>
    </UserControl.Resources>
    <Grid>
        <Grid.RowDefinitions>
            <RowDefinition Height=".8*"/>
            <RowDefinition Height=".2*"/>
        </Grid.RowDefinitions>
        <Image Source="{Binding ImageSource}"/>
        <TextBlock Grid.Row="1" Text="{Binding ImageName}" HorizontalAlignment="Center"/>
    </Grid>
</local:DragDropAdornerBase>

The three collapsed Storyboards define animations for the adorner load, as well as dropped animations for both DropStates. There is nothing in the code behind for this control. The second step is set all the DragDropHelper attached properties in the Window1.xaml.

<Window x:Class="Clarity.Demo.GenericDragDrop.Window1"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Clarity.Demo.GenericDragDrop"
    xmlns:ex1="clr-namespace:Clarity.Demo.GenericDragDrop.Example1"
    xmlns:ex2="clr-namespace:Clarity.Demo.GenericDragDrop.Example2"
    Title="Window1" Height="400" Width="500">
    <Window.Resources>
        <ex1:ImageDragDropAdorner x:Key="imageAdorner"/>
        <ex2:TextDragDropAdorner x:Key="ddAdorner"/>
    </Window.Resources>

    <Canvas>
        <TextBlock x:Name="dragSource" Text="Drag Me!" Background="CornflowerBlue" DataContext="Drag Me!" Margin="10" MaxHeight="50" MaxWidth="50"
                   local:DragDropHelper.IsDragSource="true" 
                   local:DragDropHelper.DragDropControl="{StaticResource ddAdorner}"
                   local:DragDropHelper.DropTarget="dropTarget"
                   local:DragDropHelper.AdornerLayer="adornerLayer"/>
        <Ellipse x:Name="dropTarget" Margin="200 200 0 0" Width="200" Height="100" Fill="LightGreen"/>
        <TextBlock Margin="250 240 0 0" Text="Drop Text Here"/>
        
        <Image Margin="10 100 10 0" Source="{Binding ImageSource}" Width="120" Height="120"
                local:DragDropHelper.IsDragSource="true" 
                local:DragDropHelper.DragDropControl="{StaticResource imageAdorner}"
                local:DragDropHelper.DropTarget="imageDropTarget"
                local:DragDropHelper.AdornerLayer="adornerLayer"/>
        <TextBlock x:Name="imageDropTarget" Background="Yellow" Text="Drop Album Here" Margin="200 10 0 0" Width="200" Height="150" />

        <Canvas x:Name="adornerLayer" Visibility="Collapsed"/>
    </Canvas>
</Window>

Let’s look at this XAML piece by piece. First, the Image and following TextBlock:

<Image Margin="10 100 10 0" Source="{Binding ImageSource}" Width="120" Height="120"
        local:DragDropHelper.IsDragSource="true" 
        local:DragDropHelper.DragDropControl="{StaticResource imageAdorner}"
        local:DragDropHelper.DropTarget="imageDropTarget"
        local:DragDropHelper.AdornerLayer="adornerLayer"/>
<TextBlock x:Name="imageDropTarget" Margin="200 10 0 0" Width="200" Height="150" 
           Background="Yellow" Text="Drop Album Here"/>

The first attached property sets this Image draggable. Remember, the DragDropControl property is defined as a UIElement, and here that UIElement is defined in the Window’s Resources. The DragDropControl property is set second using a StaticResource with the key “imageAdorner.”

<Window.Resources>
    <ex1:ImageDragDropAdorner x:Key="imageAdorner"/>
    <ex2:TextDragDropAdorner x:Key="ddAdorner"/>
</Window.Resources>

The third property defines the Droppable Area by setting the DropTarget to the name of the target. In this example, that target is the TextBlock named “imageDropTarget.” And finally, the Adorner Layer is defined by setting the property to the name of the Canvas added at the bottom of Window1.xaml.

And that is all it takes! A simple user control and four attached property definitions and now we can drag and drop our album cover. I encourage you to download the source and run it, but it looks a little something like this:

image 

Well, that was easy, but there’s not indication when we are over the drop target. That’s because the StateChangedHandler was not overridden in the adorner implementation. Let’s try that next.

Example2 - TextDragDropAdorner

For this example I incorporated some visual feedback for the user as to the Drag Adorner’s DropState, both a border and an icon will indicate to the user the drop state.

<local:DragDropAdornerBase x:Class="Clarity.Demo.GenericDragDrop.Example2.TextDragDropAdorner"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:local="clr-namespace:Clarity.Demo.GenericDragDrop"
    MinWidth="80" MinHeight="30"
    >
    <UserControl.RenderTransform...>
    <UserControl.Resources...>
    <UserControl.Triggers...>
    <Grid x:Name="grid" RenderTransformOrigin="0.5,0.5">
        <Grid.RenderTransform>
            <TransformGroup>
                <ScaleTransform ScaleX="1" ScaleY="1"/>
                <SkewTransform AngleX="0" AngleY="0"/>
                <RotateTransform Angle="0"/>
                <TranslateTransform X="0" Y="0"/>
            </TransformGroup>
        </Grid.RenderTransform>
        <Rectangle x:Name="back" StrokeThickness="2" RadiusX="8" RadiusY="8" Fill="#7F000000" Stroke="LightGray" />
        <ContentControl x:Name="content" Content="{Binding}" HorizontalAlignment="Center" VerticalAlignment="Center" TextBlock.FontFamily="Myriad Pro" TextBlock.FontSize="12" TextBlock.Foreground="#dcdcdc" Foreground="#FFFFFFFF" />
        <Image Opacity=".8" x:Name="indicator" Width="20" Height="20" Stretch="Uniform" HorizontalAlignment="Right" VerticalAlignment="Top" Margin="0,-12,-12,0" />
    </Grid>
</local:DragDropAdornerBase>

I’ve decided to use the Rectangle “back” Stroke property to define our border and the Image “indicator” as the icon. But this XAML doesn’t know anything about the AdornerDropState. I could use DataBinding and ValueConverters, but for the purposes of this example I didn’t.

Instead, the DragDropAdornerBase class’ virtual StateChangedHandler method is overridden in the child class code behind to provide the functionality we need.

public override void StateChangedHandler(DependencyObject d, DependencyPropertyChangedEventArgs e)
{
    TextDragDropAdorner myclass = (TextDragDropAdorner)d;

    switch ((DropState)e.NewValue)
    {
        case DropState.CanDrop:
            myclass.back.Stroke = Application.Current.Resources["canDropBrush"] as SolidColorBrush;
            myclass.indicator.Source = Application.Current.Resources["dropIcon"] as DrawingImage;
            break;
        case DropState.CannotDrop:
            myclass.back.Stroke = Application.Current.Resources["solidRed"] as SolidColorBrush;
            myclass.indicator.Source = Application.Current.Resources["noDropIcon"] as DrawingImage;
            break;
    }
}

Of course, the various brushes and icons are defined in the application resource dictionary.

Now we set the appropriate attached properties in Window1 as in the previous example (using a different drag adorner resource and a different drop target):

<TextBlock Margin="10" x:Name="dragSource" Text="Drag Me!" DataContext="Drag Me!" Background="Blue" MaxHeight="50" MaxWidth="50"
           local:DragDropHelper.IsDragSource="true" 
           local:DragDropHelper.DragDropControl="{StaticResource ddAdorner}"
           local:DragDropHelper.DropTarget="dropTarget"
           local:DragDropHelper.AdornerLayer="adornerLayer"/>

That’s all the setup. Try it out by clicking and dragging:

image

Now drag over the target area:

image

Notice that the blue border is visible and the red ‘x’ is now a yellow ‘+’? It works!

Practical Use

The code above is all we need to use generic base classes to create drag and droppable objects, which is cool, but what’s the point? The point is to manipulate the data behind the draggable object. Maybe you want to add the album to a play list, or you want to filter a list based on a word in a tag cloud, or maybe you want to trash a file - what ever the purpose, these are practical uses of drag and drop.

To support this there are two more pieces of the DragDropHelper class that we need. The first is a way to move data, and the second is a way to announce that data was moved. I’ve done this by defining an object in the helper class called “_draggedData” which is set in the DragSource_PreviewMouseLeftButtonDown method.

_draggedData = (sender as FrameworkElement).DataContext;

Now that the data is saved, we just have to broadcast that it was dropped. So, in the _adorner_MouseUp method, if the Drag Adorner is in the CanDrop DropState:

if (ItemDropped != null)
    ItemDropped(_adorner, new DragDropEventArgs(_draggedData));

Where the ItemDropped event and DragDropEventArgs are defined as:

public static event EventHandler<DragDropEventArgs> ItemDropped;
public class DragDropEventArgs : EventArgs
{
    public object Content;
    public DragDropEventArgs() { }
    public DragDropEventArgs(object content)
    {
        Content = content;
    }
}

OK, great, we’ve got the data and we’re broadcasting, but so what? No classes are holding a DragDropHelper instance and listening for the event. Except, because the event is static ANY class can register a listener without an instance. One line is all it takes to wire up the listener.

DragDropHelper.ItemDropped += new EventHandler<DragDropEventArgs>(DragDropHelper_ItemDropped);

Conclusion

With some up front work in the base classes it is now a simple matter create new implementations of a WPF drag and drop control.

Comments

lroth said:

I know some of the code runs off the page, but everything you need to see should be viewable. If you find something that isn't make a comment and I'll edit the post. Thanks for reading!

# March 31, 2009 5:28 PM

hank said:

I am working with drag and drop currently, and your post helps me a lot, thank you for sharing.

but when I run the project you supplied, I dropped the text and image, it returns to its original location instead of stopping at the destination location. do you have any suggestions? thank in advance.

# April 2, 2009 6:15 AM

lroth said:

Hank, did you run the code as is, or did you make any changes? The DropState is determined by the mouse location, so maybe try again and make sure the mouse is over the Drop Target, not just the Drag Adorner.

You could also drop a breakpoint in the StateChangedHandler to ensure that the DropState is updating correctly.

Let me know if that helps

# April 2, 2009 9:10 AM

hank said:

Hi, Iroth. Thank you very much for your response, StateChangedHandler works fine.

I want the dragsource stops at the dropTarget location, which is the implementation of this:

Window1.xaml.cs

       void DragDropHelper_ItemDropped(object sender, DragDropEventArgs e)

       {

             //how can I makes the dragged object moves to the destiny location?

       }

can you give any suggestions? thanks in advance.

# April 3, 2009 2:00 AM

lroth said:

The way this code is currently written, the object you dragged will not change. The intention was just to enable adding the object to something else.

If you have two lists, say List1 and List2, and you want to drag and item from List1 to List2 you will need to implement the ItemDropped event handler in both lists. The implementation of this handler will need to recognize which list the item came from and which was the destination list to appropriately remove/add the item. You can enable this by having a property on the data you are dragging to identify the owner, or adding an owner property to the DragDropEventArgs structure, etc.

I don't currently have a sample that does this, but if I get time in the next few days I'll create one and post about it.

# April 3, 2009 8:14 AM

hank said:

I see, Thank you Iroth, I really appreciate your help.

# April 6, 2009 2:13 AM

Like pancakes... said:

Last week I blogged about a generic Drag and Drop framework . That post focused on the framework and

# April 6, 2009 8:33 PM

IamHuM said:

Hi, Iroth,

        Nice post... I was searching for this solution.

        In this one how i can check for the dropping control dynamically. I mean to say if there are 20 textboxes and i want the item(text) to be dropped in any of the 20 textboxes then what will be modifications required in the code. When i start dragging at that time i may not know which is my target drop control(which you are setting in XAML at design time). I will come to know about my drop control when the mouse left button is realesed.

        So how to do this...? Any suggestions...

Thanks once again for the nice post.

IamHuM

# April 14, 2009 2:33 AM

lroth said:

If you want to specify multiple potential drop targets you will have to modify the DragDropHelper class. There are two things you can do:

1 - forget about keeping it generic, and hard-code those drop targets into the helper class. You will have to store a List<UIElement> instead of a single UIElement. or

2 - still storing the drop targets as a List<UIElement>, allow the attached property to take a list of target names and parse the list to find them all.

Hope that helps.

# April 14, 2009 8:53 PM

Poluxx said:

Many thanks for the source and the explanation. The code is clear and the idea ingenious :)

# April 17, 2009 8:39 AM

chris said:

As a newbie, this sample is really helpful, thanks! I am trying to add code to trigger a storyboard to begin when the item is dropped. I've added my animation to the TextDragDropAdorner.xaml, but can't quite figure out how to call it.

I've tried the following in the in _adorner_MouseUp in the DragDropHelper.cs:

((Storyboard)_adorner.Resources["showMessage"]).Begin(_topWindow);

Any advice?

thx!

# April 20, 2009 1:02 PM

IamHuM said:

Hi Iroth,

Thanks for the reply. I am newbie so if its possible for you then can you post some sample code to make the source and destination controls dynamic. I want my source ListView to be loaded in code and similarly drop list also to be dynamic.

Thanks,

IamHuM

# April 21, 2009 10:05 AM

lroth said:

Chris,

Make sure you named your Storyboard "showMessage" in the UserControl.Resources of the TextDragDropAdorner. Then, change that begin call to pass the adorner into the Storyboard's Begin method.

((Storyboard)_adorner.Resources["canDrop"]).Begin(_adorner);

As I understand it, because the Storyboard is defined within the adorner, and not the explicitly in the window, the adorner should be passed in as the containingObject parameter. You can read more on MSDN (msdn.microsoft.com/.../ms605709.aspx)

# April 21, 2009 8:18 PM

Allen said:

Excellent post. Moved me well beyond Bea's post.

I found a small bit of a cosmetic bug with your mouse capture. The capture fails If the mouse moves outside of the source element before IsMovementBigEnough == true. I fixed this by capturing the mouse to the source element while checking for IsMovementBigEnough. Once it returns true, drop the capture and give it to the adorner.

Also had to add code is the DragSource_PreviewMouseLeftButtonUp to release capture when necessary.

If anyone is interested, I'm currently working on an even more generic version. I've turned DragData into an attached property so that it can be bound to whatever in XAML rather than hardwired as the DataContext and have also converted the ItemDropped event into an attached event. I'm still working on letting the dropTarget decide that it's a drop target rather than the source.

# June 20, 2009 1:09 AM

Morten said:

This certainly is looking like a good starting point..

I will have to make a few changes, though, as most if not all of my use cases involve draggin stuff around in a TreeView with the possibility of dropping something between two existing nodes rather than just into a different element alltogether.

But thank you for the groundwork :)

# January 8, 2010 7:26 AM

YippeeSoft???????????? » Blog Archiv » WPF Toolkit said:

Pingback from  YippeeSoft????????????  &raquo; Blog Archiv   &raquo; WPF Toolkit

# February 24, 2010 12:13 AM

XOR said:

How would I do the following:

local:DragDropHelper.IsDragSource="true"

                  local:DragDropHelper.DragDropControl="{StaticResource ddAdorner}"

                  local:DragDropHelper.DropTarget="dropTarget"

from code-behind?  I'm dynamically generating Controls that need to have the drag/drop functionality.

Thanks!

# March 5, 2010 12:06 PM
Leave a Comment

(required) 

(required) 

(optional)

(required)