Title: Move and resize multiple shapes in WPF and C#
The example Move and resize multiple rectangles in WPF and C# shows how to let the user move and resize multiple rectangles in a WPF program. This example extends that one to let you move and resize multiple shapes.
See the earlier example for the basic idea.
Instead of storing a list of rectangles that you can move, this example stores a list containing multiple shapes that you can move. The following code declares and initializes the list.
// The shapes that the user can move and resize.
private List<Shape> Shapes;
// Make a list of the shapes that the user can move.
private void Window_Loaded(object sender, RoutedEventArgs e)
{
Shapes = new List<Shape>();
foreach (UIElement child in canvas1.Children)
{
if (child is Shape)
Shapes.Add(child as Shape);
}
// Reverse the list so the shapes on top come first.
Shapes.Reverse();
}
The program's window holds a Canvas control that contains an Ellipse, Polygon, and Rectangle. It also contains a Border to show that you don't need to let the user move every control.
The window's Loaded event handler creates the list and then loops through the Canvas control's children. It adds the Shape controls that it finds to the Shapes list.
After it has examined all of the children, the method reverses the order of the Shapes list so they appear inside the list in top-to-bottom order.
The following method determines what if anything is at a particular point.
// The part of the rectangle under the mouse.
private HitType MouseHitType = HitType.None;
// The shape that was hit.
private Shape HitShape = null;
// If the point is over any shape,
// return the shape and the hit type.
private void FindHit(Point point)
{
HitShape = null;
MouseHitType = HitType.None;
foreach (Shape shape in Shapes)
{
MouseHitType = SetHitType(shape, point);
if (MouseHitType != HitType.None)
{
HitShape = shape;
return;
}
}
// We didn't find a hit.
return;
}
This method loops through the Shapes list and calls the SetHitType method for each of the controls in the list. If SetHitType finds that the target point is over a shape, then FindHit saves the hit type and the shape hit in form-level variables and exits.
Much of the rest of the program is similar to the earlier example. The SetHitType method compares the mouse's position to the shape's left, right, top, and bottom coordinates to see if the point is over one of the shape's corners, edges, or body.
One big change to the method is how it finds the shape's left, right, top, and bottom coordinates. The earlier version used Canvas.GetLeft and Canvas.GetTop to get the shape's left and top coordinates. That works for most shapes but doesn't work for polygons.
A polygon's Points list contains the points that make up the polygon. The coordinates of those points are relative to the polygon's left and top coordinates within the Canvas control, so the program must add the Points coordinates to the left and top values to see where the polygon actually lies.
The new version of the SetHitType method calls the following GetLRTB method to get the shape's actual position.
// Return the shape's left, right, top, and bottom.
private void GetLRTB(Shape shape,
out double left, out double right,
out double top, out double bottom)
{
if (!(shape is Polygon))
{
left = Canvas.GetLeft(shape);
top = Canvas.GetTop(shape);
right = left + shape.ActualWidth;
bottom = top + shape.ActualHeight;
return;
}
// Handle polygons separately.
Polygon polygon = shape as Polygon;
left = polygon.Points[0].X;
right = left;
top = polygon.Points[0].Y;
bottom = top;
foreach (Point point in polygon.Points)
{
if (left > point.X) left = point.X;
if (right < point.X) right = point.X;
if (top > point.Y) top = point.Y;
if (bottom < point.Y) bottom = point.Y;
}
// Add the polygon's left and top coordinates.
left += Canvas.GetLeft(shape);
right += Canvas.GetLeft(shape);
top += Canvas.GetTop(shape);
bottom += Canvas.GetTop(shape);
}
If the shape is not a polygon, this method uses the same method used by the previous program. It uses the Canvas class's GetLeft and GetTop methods to get the shape's location on the Canvas control. It then adds the shape's actual width and height to get its right and bottom edges.
If the shape is a polygon, the method loops through its points to find the largest and smallest X and Y coordinates. It then adds the shape's left and top positions on the Canvas control.
The rest of the program is similar to the earlier example, although it has some problems. First, it doesn't work with polygons. We need to modify the code to adjust the polygon's points rather than changing its left and top coordinates on the Canvas control. I'll fix that in my next post.
Second, the program may think it has hit part of a shape when the mouse is not actually over the shape. For example, when the mouse is over the upper left corner of an ellipse's bounding rectangle, it is not actually over the ellipse itself. In fact, the mouse may be over another shape that lies below the ellipse and the program will still think the mouse is over the ellipse. This wasn't a problem when we were moving only rectangles because a rectangle coincides with its bounding rectangle. Right now I don't see a great need to improve this part of the program so I'm not going to fix this.
Download the example to experiment with it and to see additional details. If you like, try to fix the program so it can move and resize polygons. I'll show my solution in my next post.
Download the example to experiment with it and to see additional details.
|