Make drawing extension methods in WPF and C#

[example]

This example shows how to make a few handy extension methods for drawing in WPF. Drawing in WPF is at best awkward and at worst downright painful.


For example, the following XAML code draws a rectangle.

<Grid>
    <Canvas Name="canDrawing">
        <Rectangle Canvas.Left="100" Canvas.Top="30"
            Width="100" Height="70" Fill="LightGreen"
            Stroke="Green" StrokeThickness="5"/>
    </Canvas>
</Grid>

This is awkward but survivable. The following code shows how you can use C# code to draw the same rectangle.

Rectangle rectangle = new Rectangle();
Canvas.SetLeft(rectangle, 100);
Canvas.SetTop(rectangle, 30);
rectangle.Width = 100;
rectangle.Height = 70;
rectangle.Fill = Brushes.LightGreen;
rectangle.Stroke = Brushes.Green;
rectangle.StrokeThickness = 5;
canDrawing.Children.Add(rectangle);

This code creates a new Rectangle object. The Left and Top properties are attached properties provided by the Canvas class, not properties of the rectangle. That means you cannot simply set the properties for the rectangle. Instead you must use the Canvas class’s SetLeft and SetTop methods to set those values. (I get why they did it this way. The Left and Top properties only make sense if the rectangle is inside some other control, but why didn’t they just give shape objects their own properties?)

The code then sets the rectangle’s Width, Height, Fill, Stroke, and StrokeThickness properties. Finally, the program adds the rectangle to the Canvas control’s child list.

All of this is manageable, but awkward. It would be nice if the Canvas class provided a method for creating a Rectangle object and setting at least some of its properties.

The following code shows an extension method that does that.

public static class DrawingExtensions
{
    // Add a Rectangle to a Canvas.
    public static Rectangle DrawRectangle(
        this Canvas canvas, Brush fill, Brush stroke,
        double stroke_thickness, double left,
        double top, double width, double height)
    {
        Rectangle rectangle = new Rectangle();
        Canvas.SetLeft(rectangle, left);
        Canvas.SetTop(rectangle, top);
        rectangle.Width = width;
        rectangle.Height = height;
        rectangle.Fill = fill;
        rectangle.Stroke = stroke;
        rectangle.StrokeThickness = stroke_thickness;
        canvas.Children.Add(rectangle);
        return rectangle;
    }
}

Recall that extension methods must be contained in public static classes. that’s why this method is inside the DrawingExtensions class.

The method must also be static. Its first parameter is marked with the this keyword to indicate that the method extends that object. In this case, the method extends the Canvas class.

The body of the method simply performs the same steps shown earlier that use C# code to create a new Rectangle object, set its parameters, and add it to the Canvas control’s Children collection.

The method finishes by returning the new Rectangle object in case the calling code needs to manipulate it. For example, that code could change the object’s properties or set other properties.

The main program can use this method as in the following.

rect = new Rect(50, 80, 200, 20);
rectangle = canDrawing.DrawRectangle(Brushes.Pink,
    Brushes.Red, 5, 100, 30, 100, 70);
rectangle.RadiusX = 10;
rectangle.RadiusY = 10;

This code uses the extension method to create the Rectangle. It then changes the returned Rectangle object’s RadiusX and RadiusY properties to give the rectangle rounded corners.

Sometimes it may be convenient to define a Rectangle by giving a Rect that defines its size and position. The following code shows an extension method

public static Rectangle DrawRectangle(
    this Canvas canvas, Brush fill, Brush stroke,
    double stroke_thickness, Rect rect)
{
    return canvas.DrawRectangle(fill, stroke,
        stroke_thickness, rect.Left, rect.Top,
        rect.Width, rect.Height);
}

This method simply invokes the previous version of the extension method, using the Rect parameter’s properties to fill in the rectangle’s size and position.

The example program also defines extension methods to draw ellipses. Those methods are so similar to the DrawRectangle extension methods that I won’t show them here. Download the example to see the details.

The following code shows the DrawLine extension method.

public static Line DrawLine(
    this Canvas canvas, Brush stroke,
    double stroke_thickness, double x1, double y1,
    double x2, double y2)
{
    Line line = new Line();
    line.X1 = x1;
    line.Y1 = y1;
    line.X2 = x2;
    line.Y2 = y2;
    line.Stroke = stroke;
    line.StrokeThickness = stroke_thickness;
    canvas.Children.Add(line);
    return line;
}

This method creates a Line object and sets its X1, Y1, X2, and Y2 properties. It sets the stroke properties and returns the new object.

The following code shows the DrawPolyline extension method.

public static Polyline DrawPolyline(
    this Canvas canvas, Brush stroke,
    double stroke_thickness,
    IEnumerable<Point> points)
{
    Polyline polyline = new Polyline();
    polyline.Points = new PointCollection(points);
    polyline.Stroke = stroke;
    polyline.StrokeThickness = stroke_thickness;
    canvas.Children.Add(polyline);
    return polyline;
}

This method is similar to the others. It creates the new drawing object, sets its properties, and returns the new object. This method is a bit different because it needs to define object’s Points property. That property must be set to a PointCollection object. Fortunately that object’s constructor can take an IEnumerable<Point> as a parameter so initializing the points is easy.

Most of the example’s main program is simple because it uses these extension methods. For example, the following code shows how the program makes the face’s left eye and pupil.

// Left eye.
rect = new Rect(100, 110, 40, 50);
canDrawing.DrawEllipse(Brushes.White,
    Brushes.Black, 5, rect);

// Left pupil.
rect = new Rect(120, 120, 20, 30);
canDrawing.DrawEllipse(Brushes.Black,
    Brushes.Black, 5, rect);

Download the example to see the rest of the code. In particular, the program uses a loop to define the points that make up the mustache’s polyline.


Download Example   Follow me on Twitter   RSS feed   Donate




About RodStephens

Rod Stephens is a software consultant and author who has written more than 30 books and 250 magazine articles covering C#, Visual Basic, Visual Basic for Applications, Delphi, and Java.
This entry was posted in drawing, extension methods, graphics, wpf, XAML and tagged , , , , , , , , , . Bookmark the permalink.

2 Responses to Make drawing extension methods in WPF and C#

  1. Angel says:

    Great example. Very useful

    P.S.
    in the menu above in “Contac Me”, I have tried to send you a message to inform you of a link that does not work in “http://www.csharphelper.com/” specifically “Blog”. But to be able to send it, it asks me to install the “Really Simple CAPTCHA” plugin. It does not seem logical to me that to report an error in your Web, it is necessary to install a plugin in my computer
    a greeting

    • RodStephens says:

      Thanks for pointing out the broken link. I have fixed it.

      The CAPTCHA thing is just the way that the tool that I use to manage the site does it. Annoying but the web site’s tools have blocked thousands of spam messages.

      Posting a comment the way you did works, too, though.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.