loris loris - 10 months ago 78
C# Question

How to drag a UserControl inside a Canvas

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?

Answer Source

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

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

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.

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