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