tags:

views:

1961

answers:

3

I'm trying to create a WPF application where I can drag an image around.

Currently I have an image placed in the center of the window, and I'm thinking of using the three mouseevents MouseDown, MouseMove and MouseUp to calculate the new position when dragging the image.

Are there any other good ideas on how to do this? I'm totally new to WPF so my mindset is still in the Windows Forms world.

As far as I can see I need to use a in order to have absolute positioning available.

A: 

Doing the usual mouse up/down/move that you would do in forms is just as valid under WPF, however, you could wrap the facility to drag into an attached dependency property to inject this behaviour simply by setting a property - that would be cool.

I'll try and pull a code sample together for how to do that.

deepcode.co.uk
+10  A: 

ok, here's an attached property "behaviour" that you can use to make any element draggable provided it's on a canvas:

public class DraggableExtender : DependencyObject
{
    // This is the dependency property we're exposing - we'll 
    // access this as DraggableExtender.CanDrag="true"/"false"
    public static readonly DependencyProperty CanDragProperty =
        DependencyProperty.RegisterAttached("CanDrag",
        typeof(bool),
        typeof(DraggableExtender),
        new UIPropertyMetadata(false, OnChangeCanDragProperty));

    // The expected static setter
    public static void SetCanDrag(UIElement element, bool o)
    {
        element.SetValue(CanDragProperty, o);
    }

    // the expected static getter
    public static bool GetCanDrag(UIElement element)
    {
        return (bool) element.GetValue(CanDragProperty);
    }

    // This is triggered when the CanDrag property is set. We'll
    // simply check the element is a UI element and that it is
    // within a canvas. If it is, we'll hook into the mouse events
    private static void OnChangeCanDragProperty(DependencyObject d, 
              DependencyPropertyChangedEventArgs e)
    {
        UIElement element = d as UIElement;
        if (element == null) return;

        if (e.NewValue != e.OldValue)
        {
            if ((bool)e.NewValue)
            {
                element.PreviewMouseDown += element_PreviewMouseDown;
                element.PreviewMouseUp += element_PreviewMouseUp;
                element.PreviewMouseMove += element_PreviewMouseMove;
            }
            else
            {
                element.PreviewMouseDown -= element_PreviewMouseDown;
                element.PreviewMouseUp -= element_PreviewMouseUp;
                element.PreviewMouseMove -= element_PreviewMouseMove;
            }
        }
    }

    // Determine if we're presently dragging
    private static bool _isDragging = false;
    // The offset from the top, left of the item being dragged 
    // and the original mouse down
    private static Point _offset;

    // This is triggered when the mouse button is pressed 
    // on the element being hooked
    static void element_PreviewMouseDown(object sender,
            System.Windows.Input.MouseButtonEventArgs e)
    {
        // Ensure it's a framework element as we'll need to 
        // get access to the visual tree
        FrameworkElement element = sender as FrameworkElement;
        if (element == null) return;

        // start dragging and get the offset of the mouse 
        // relative to the element
        _isDragging = true;
        _offset = e.GetPosition(element);
    }

    // This is triggered when the mouse is moved over the element
    private static void element_PreviewMouseMove(object sender, 
              MouseEventArgs e)
    {
        // If we're not dragging, don't bother - also validate the element
        if (!_isDragging) return;

        FrameworkElement element = sender as FrameworkElement;
        if (element == null) return;

        Canvas canvas = element.Parent as Canvas;
        if( canvas == null ) return;

        // Get the position of the mouse relative to the canvas
        Point mousePoint = e.GetPosition(canvas);

        // Offset the mouse position by the original offset position
        mousePoint.Offset(-_offset.X, -_offset.Y);

        // Move the element on the canvas
        element.SetValue(Canvas.LeftProperty, mousePoint.X);
        element.SetValue(Canvas.TopProperty, mousePoint.Y);
    }

    // this is triggered when the mouse is released
    private static void element_PreviewMouseUp(object sender, 
            MouseButtonEventArgs e)
    {
        _isDragging = false;
    }

}

You can then use this in your XAML by importing the namespace your class is contained in (something like this:)

<Window x:Class="WPFFunWithDragging.Window1"
        xmlns:local="clr-namespace:WPFFunWithDragging" .. >

And then you can just set DraggableExtender.CanDrag="true" on elements to drag around:

<Canvas>
    <Image Source="Garden.jpg" 
           Width="50" 
           Canvas.Left="10" Canvas.Top="10" 
           local:DraggableExtender.CanDrag="true"/>
</Canvas>

Hope this is of some use :)

deepcode.co.uk
Wow, thanks for the good answer!
Frode Lillerud
Thanks - very useful code. I would improve it slightly by having the element capture the mouse in the mouse down and release the mouse capture in the mouse up. Otherwise it is easy to miss mouse moves if for some reason the mouse falls outside of the bounds of the item being dragged.
Martin Randall
Good point - I've just done a similar thing for chromeless windows, will add that suggestion ;)
deepcode.co.uk
This is great, thanks! I was just about to post this same question, but StackOverflow kindly showed me this duplicate with a excellent answer.
Bruce
A: 

Wow, really cool stuff! Why doesn't it work with other containers though, like Viewbox?

Larry