0 Comments

I recently needed to support dragging shapes on a Canvas in WPF. There are a few detailed articles on this you can read over at CodeProject (see here and here for example). However, I just needed something very simple, so here’s a short code snippet that you can try out using my favourite prototyping tool LINQPad:

var w = new Window();
w.Width = 600;
w.Height = 400;
var c = new Canvas();

Nullable<Point> dragStart = null;

MouseButtonEventHandler mouseDown = (sender, args) => {
    var element = (UIElement)sender;
    dragStart = args.GetPosition(element); 
    element.CaptureMouse();
};
MouseButtonEventHandler mouseUp = (sender, args) => {
    var element = (UIElement)sender;
    dragStart = null; 
    element.ReleaseMouseCapture();
};
MouseEventHandler mouseMove = (sender, args) => {
    if (dragStart != null && args.LeftButton == MouseButtonState.Pressed) {    
        var element = (UIElement)sender;
        var p2 = args.GetPosition(c);
        Canvas.SetLeft(element, p2.X - dragStart.Value.X);
        Canvas.SetTop(element, p2.Y - dragStart.Value.Y);
    }
};
Action<UIElement> enableDrag = (element) => {
    element.MouseDown += mouseDown;
    element.MouseMove += mouseMove;
    element.MouseUp += mouseUp;
};
var shapes = new UIElement [] {
    new Ellipse() { Fill = Brushes.DarkKhaki, Width = 100, Height = 100 },
    new Rectangle() { Fill = Brushes.LawnGreen, Width = 200, Height = 100 },
};


foreach(var shape in shapes) {
    enableDrag(shape);
    c.Children.Add(shape);
}

w.Content = c;
w.ShowDialog();

The key is that for each draggable shape, you handle MouseDown (to begin a mouse “capture”), MouseUp (to end the mouse capture), and MouseMove (to do the move). Obviously if you need dragged objects to come to the top in the Z order, or to be able to auto-scroll as you drag, you’ll need to write a bit more code than this. The next obvious step would be to turn this into an “attached behaviour” that you can add to each object you put onto your canvas.

Vote on HN

Comments

Comment by blorq

You also have to deal with losing the mouse outside of the window and capture loss.

I have also found this method to be poorly performing, as those properties effect layout. I might suggest trying with a render transform.

Comment by Mark H

Yes, this method lets you move things out of visible space. In what situations are you getting capture loss?

Comment by Cortran

Great!!!!!!

Cortran
Comment by Unknown

Very nice - simple and elegant. I like your use of the Action<> to encapsulate "dragability" and apply it to arbitrary elements.
Thanks, you've nudged me in a very good direction!

comments powered by Disqus