WPF: Drag Drop Adorner


In my previous post, I created a Decorator for Dragging and Dropping between ItemsControls. The one thing it was lacking is visual feedback. There’s a way to provide that visual feedback and it’s by using Adorners. In short, adorners are bound to a UIElement, and it sits on an AdornerLayer of that bound element. The AdornerLayer has a Z-index that sit on top of your adorned element and renders independently, perfect for providing visual cues/feedback for state changes like dragging, resize, rotation, etc. I basically worked off my previous example and added this feature into my code.

DragAdorner

To create a DragAdorner, you need to subclass the Adorner class under System.Windows.Document namespace, and it takes a reference of the UIElement you want to adorn. There are many ways to “create” your adorner’s look. One way is to use a VisualBrush to “clone” your UIElement’s look, and set it to a Rectangle’s Fill property along with the desired width and height. In my version, because I would like my adorner’s look to be mapped to the data layout of the destination target, I created a new Dependency Property of type DataTemplate in my DragDropDecorator.

With this, I can now specify the DataTemplate to be binded to each Decorator, like so.

<dd:ItemsControlDragDropDecorator Grid.Column="0" Grid.Row="1" AllowDrop="True"
        ItemType="{x:Type this:Product}" DataTemplate="{StaticResource listBoxDataTemplate}" >
    <ListBox x:Name="listbox" Margin="10" ItemTemplate="{StaticResource listBoxDataTemplate}" />
</dd:ItemsControlDragDropDecorator>

<dd:ItemsControlDragDropDecorator Grid.Column="1" Grid.Row="1" AllowDrop="True"
        ItemType="{x:Type this:Product}" DataTemplate="{StaticResource tabItemContentTemplate}" >
    <TabControl x:Name="tabcontrol" Margin="10"
                ItemContainerStyle="{StaticResource tabControlItemStyle}" />
</dd:ItemsControlDragDropDecorator>

Now I can pass this DataTemplate value to my DragAdorner, and bind that to a ContentPresenter. This will create the desired effect I want. This is the implementation of my DragAdorner class.

public class DragAdorner : Adorner
{
    private ContentPresenter _contentPresenter;
    private AdornerLayer _adornerLayer;
    private double _leftOffset;
    private double _topOffset;

    public DragAdorner(object data, DataTemplate dataTemplate, UIElement adornedElement, AdornerLayer adornerLayer)
        : base(adornedElement)
    {
        _adornerLayer = adornerLayer;

        _contentPresenter = new ContentPresenter()
            { Content = data, ContentTemplate = dataTemplate, Opacity = 0.75 };

        _adornerLayer.Add(this);
    }

    protected override Size MeasureOverride(Size constraint)
    {
        _contentPresenter.Measure(constraint);
        return _contentPresenter.DesiredSize;
    }

    protected override Size ArrangeOverride(Size finalSize)
    {
        _contentPresenter.Arrange(new Rect(finalSize));
        return finalSize;
    }

    protected override Visual GetVisualChild(int index)
    {
        return _contentPresenter;
    }

    protected override int VisualChildrenCount
    {
        get { return 1; }
    }

    public void UpdatePosition(double left, double top)
    {
        _leftOffset = left;
        _topOffset = top;
        if (_adornerLayer != null)
        {
            _adornerLayer.Update(this.AdornedElement);
        }
    }

    public override GeneralTransform GetDesiredTransform(GeneralTransform transform)
    {
        GeneralTransformGroup result = new GeneralTransformGroup();
        result.Children.Add(base.GetDesiredTransform(transform));
        result.Children.Add(new TranslateTransform(_leftOffset, _topOffset));
        return result;
    }

    public void Destroy()
    {
        _adornerLayer.Remove(this);
    }
}

To activate the DragAdorner, you will need to hook up to the PreviewDragEnter, PreviewDragOver and PreviewDragLeave events. In these events, you will instantiate the DragAdorner and update it’s positions during DragEnter and DragOver, and finally destroying it when DragLeave or DragDrop occurs. You use the UpdatePosition and Destroy methods in the DragAdorner class to do that.

In the meantime, I’m going to explore how to add dragging functionality (with visual cues) between items in the ItemsControl into the DragDropDecorator. Til then, happy coding.

Download the code sample here.

**For the fully evolved version of this, please see this post.

Share this post:
About these ads
Posted in WPF. 6 Comments »

6 Responses to “WPF: Drag Drop Adorner”

  1. abcd Says:

    how to stop the scale transform of an rectangle adorner added around the controls on canvas. the rectangle adorner is added like this protected override void OnRender(DrawingContext drawingContext)
    {
    base.OnRender(drawingContext);

    drawingContext.DrawRectangle(Brushes.Transparent,
    new Pen(Brushes.Gray, 2),
    new Rect(new Point(0, 0), DesiredSize));

    }

  2. Jalal Says:

    Thanks!

  3. Vijay BK Says:

    Thanks for the solution. I wanted to implement something similar for the mouse move without the left button down, not related to drag and drop. This helped me a lot.

  4. amit Says:

    Does this work on Canvas or is it only for Grid?

  5. The TabControl with WPF « Have Fun Coding Says:

    [...] reach this result, I used the code I found here and I changed it to adapt it to my [...]


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

Follow

Get every new post delivered to your Inbox.

Join 97 other followers

%d bloggers like this: