tags:

views:

384

answers:

1

Hi, I'm writing my first WPF application. I have a Canvas in which user can add UserControl subclasses containing a form. User should be able to drag these UserControl around the Canvas. What's the best practice to do this with WPF? Thanks.

+6  A: 

This is done in silverlight and not in WPF, but it should work.

  1. Create two private properties on the control:

    protected bool isDragging;
    private Point clickPosition;

Then attatch some event handlers in the constructor of the control:

this.MouseLeftButtonDown += new MouseButtonEventHandler(Control_MouseLeftButtonDown);
this.MouseLeftButtonUp += new MouseButtonEventHandler(Control_MouseLeftButtonUp);
this.MouseMove += new MouseEventHandler(Control_MouseMove);

Now create those methods:

private void Control_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
    isDragging = true;
    var draggableControl = sender as UserControl;
    clickPosition = e.GetPosition(this);
    draggableControl.CaptureMouse();
}

private void Control_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
    isDragging = false;
    var draggable = sender as UserControl;
    draggable.ReleaseMouseCapture();
}

private void Control_MouseMove(object sender, MouseEventArgs e)
{
    var draggableControl = sender as UserControl;

    if (isDragging && draggableControl != null)
    {
        Point currentPosition = e.GetPosition(this.Parent as UIElement);

        var transform = draggableControl.RenderTransform as TranslateTransform;
        if (transform == null)
        {
            transform = new TranslateTransform();
            draggableControl.RenderTransform = transform;
        }

        transform.X = currentPosition.X - clickPosition.X;
        transform.Y = currentPosition.Y - clickPosition.Y;
    }
}

A few things to note here:
1. This does not have to be in a canvas. It can be in a stackpanel, or grid as well.
2. This makes the entire control draggable, that means if you click anywhere in the control and drag it will drag the whole control. Not sure if thats exactly what you want.

Edit-
Expanding on some of the specifics in your question: The best way that I would implement this is to create a class that inherits from UserControl, maybe called DraggableControl that is built with this code, then all draggable controls should extend the DraggableControl.

Edit 2 - There is small issue when you have a datagrid in this control. If you sort a column in the datagrid the MouseLeftButtonUp event never fires. I have updated the code so that isDragging is protected. I found the best solution is to tie this anonymous method to the LostMouseCapture event of the datagrid:

this.MyDataGrid.LostMouseCapture += (sender, e) => { this.isDragging = false; };
Corey Sunwold
Great answer! Exactly what i searched for. Copy)
Marcel Benthin
Well done! With some minor changes in mouse move handler it works perfectly. Thanks a lot!
loris
@loris: I am interested to hear what changes you needed to make and why you made them. I have used this exact code in a few projects already and it worked fine. Maybe there is something I overlooked.
Corey Sunwold
This really helped me. Thank you very much for posting this.
Stacey
"This does not have to be in a canvas." ... Why this restriction?
serhio
The question, however, referred to a Canvas like the parent... Is there complicated to change this logic for a Canvas parent?
serhio
@serhio - This should work as is regardless of the parent.
Corey Sunwold
@Corey: No, it have some strange "delta", when mouseDown in a Canvas. You yourself wrote: "1. This does not have to be in a canvas."
serhio