object's Left and Top attached properties, and its Width, and Height properties.
private static void SetBounds(this Shape shape,
double left, double top, double width, double height)
{
Canvas.SetLeft(shape, left);
Canvas.SetTop(shape, top);
shape.Width = width;
shape.Height = height;
}
The following overloaded version of SetBounds lets you define the bounds by passing a Rect into the method.
private static void SetBounds(this Shape shape, Rect rect)
{
shape.SetBounds(rect.Left, rect.Top, rect.Width, rect.Height);
}
The following PrepareGraphicProperties method sets an object's graphical properties Fill, Stroke, StrokeThickness, and StrokeDashArray.
private static void PrepareGraphicProperties(this Shape shape,
Brush fill, Brush stroke,
double stroke_thickness, DoubleCollection stroke_dash_array)
{
shape.Fill = fill;
shape.Stroke = stroke;
shape.StrokeThickness = stroke_thickness;
if (stroke_dash_array != null)
{
shape.StrokeDashArray = stroke_dash_array;
}
}
This method extends the Shape class. It simply sets the shape's Fill, Stroke, and StrokeThickness properties. Then if the stroke_dash_array parameter is not null, it also sets the shape's StrokeDashArray property.
Now the following PrepareShape method uses the SetBounds and PrepareGraphicProperties methods to prepare a shape for use.
private static void PrepareShape(this Shape shape,
double left, double top, double width, double height,
Brush fill, Brush stroke,
double stroke_thickness, DoubleCollection stroke_dash_array)
{
shape.SetBounds(left, top, width, height);
shape.PrepareGraphicProperties(fill, stroke,
stroke_thickness, stroke_dash_array);
}
The following code shows the remaining support methods.
public static Point GetCenter(this Shape shape)
{
double cx = (Canvas.GetLeft(shape) + Canvas.GetRight(shape)) / 2.0;
double cy = (Canvas.GetTop(shape) + Canvas.GetBottom(shape)) / 2.0;
return new Point(cx, cy);
}
public static Rect GetBounds(this Shape shape)
{
return new Rect(
Canvas.GetLeft(shape), Canvas.GetTop(shape),
shape.Width, shape.Height);
}
// Position shape1 over shape2.
public static void MatchBounds(this Shape shape1, Shape shape2)
{
shape1.SetBounds(shape2.GetBounds());
}
The GetCenter method returns the object's center.
GetBounds returns a Rect giving the object's center.
MatchBounds resizes and moves object shape1 so it has the same position and bounds as shape2. For example, you could use this to position a label on top of an ellipse or rectangle.
Notice how this method uses the overloaded version of SetBounds that takes a Rect as a parameter.
Rectangles
With those helper methods in place, it's fairly easy to make extension methods that create Rectangle objects. The following method creates a new Rectangle object on a Canvas control.
public static Rectangle DrawRectangle(this Canvas canvas,
double left, double top, double width, double height,
Brush fill, Brush stroke,
double stroke_thickness, DoubleCollection stroke_dash_array)
{
Rectangle rectangle = new Rectangle();
rectangle.PrepareShape(left, top, width, height,
fill, stroke, stroke_thickness, stroke_dash_array);
canvas.Children.Add(rectangle);
return rectangle;
}
This method simply creates a new Rectangle, calls PrepareShape to set its properties, adds it to the Shape control's Children collection, and returns the result so you can use it in your program if you like.
The example also defines two overloaded versions that don't take dash and thickness parameters in case you want to use defaults for those.
In addition to drawing rectangles, the example defines extension methods to draw the following shapes:
- Rectangle
- Ellipse
- Polygon
- Polyline
- Label
- Line
- Dot
The next section says more about dots.
Dots
You can download the example to see how it draws other shapes, but I want to give a little more details for dots. It seems like I often need to draw a dot: a circle with a given radius and centered at a position. The following method makes that easy.
public static Ellipse DrawDot(this Canvas canvas,
double cx, double cy, double radius,
Brush fill, Brush stroke,
double stroke_thickness, DoubleCollection stroke_dash_array)
{
return canvas.DrawEllipse(
cx - radius, cy - radius, 2.0 * radius, 2.0 * radius,
fill, stroke, stroke_thickness, stroke_dash_array);
}
This code simply calls the DrawEllipse extension method, passing it the necessary parameters. That method is similar to the DrawRectangle method decsribed earlier, so I won't show it here.
The other thing I often do (and the inspiration for this post) is to draw a label inside a circle. The following overloaded version of DrawDot does that.
public static Ellipse DrawDot(this Canvas canvas,
out Label label,
double cx, double cy, double radius,
Brush fill, Brush stroke,
double stroke_thickness, DoubleCollection stroke_dash_array,
string text, string font_family, double font_size,
FontWeight font_weight, FontStyle font_style,
Brush border_brush, Thickness border_thickness,
Brush background_brush, Brush foreground_brush)
{
Ellipse ellipse = canvas.DrawDot(
cx, cy, radius,
fill, stroke, stroke_thickness, stroke_dash_array);
label = canvas.DrawLabel(
cx - radius, cy - radius, 2.0 * radius, 2.0 * radius,
text, font_family, font_size,
font_weight, font_style,
border_brush, border_thickness,
background_brush, foreground_brush,
HorizontalAlignment.Center, VerticalAlignment.Center);
return ellipse;
}
This method calls the previous version of DrawDot and saves the result. It then calls the DrawLabel extension method (not shown) to draw a label with the same bounds as the Ellipse that it just drew. It returns the Ellipse and returns the Label through an out parameter.
Summary
These extension methods make it easier to add shapes to a Canvas control. The example includes methods for adding Ellipse, Rectangle, Polygon, Polyline, Label, Line, and Dot shapes. Feel free to use the example as a template for making your own methods to easily add other shapes or controls.
Download the example to experiment with it and to see additional details.