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.
4 comments:
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.
Yes, this method lets you move things out of visible space. In what situations are you getting capture loss?
Great!!!!!!
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!
Post a Comment