Title: Draw lines with custom end caps in C#
This example shows how you can add custom end caps to lines. When you use a Pen object to draw lines and curves, you can set its CustomStartCap and CustomEndCap properties to determine how the ends of the line are drawn. Those properties take values of type CustomLineCap.
A CustomLineCap can represent a shape that is either filled or outlined but not both. You define the shape and determine whether it is filled or outlined by passing a GraphicsPath object to its constructor. The constructor takes two parameters representing the fill path and stroke path, and one of those should be null. Furthermore if you use a fill path, it should intercept the negative Y axis. (Yes this is all rather arcane. See CustomLineCap.CustomLineCap(GraphicsPath, GraphicsPath) Constructor for more details.)
The GraphicsPath object's coordinate system is oriented with respect to the line. The Y axis increases in the direction of the line and the X axis points to the left, as shown in the picture on the right.
The units for the GraphicsPaths are measured in line thicknesses, so the result scales if the Pen is thick or if the Graphics object on which you're drawing is transformed. For example, suppose a GraphicsPath contains a circle of radius 5. If the program uses the custom line cap with a Pen that has thickness 10, then the result is a circle with a radius of 5 × 10 = 50 pixels on the screen.
The following code shows how the example draws.
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.SmoothingMode = SmoothingMode.AntiAlias;
// Make a GraphicsPath to define the start cap.
using (GraphicsPath start_path = new GraphicsPath())
{
start_path.AddArc(-2, 0, 4, 4, 180, 180);
// Make the start cap.
using (CustomLineCap start_cap =
new CustomLineCap(null, start_path))
{
// Make a GraphicsPath to define the end cap.
using (GraphicsPath end_path = new GraphicsPath())
{
end_path.AddLine(0, 0, -2, -2);
end_path.AddLine(0, 0, 2, -2);
// Make the end cap.
using (CustomLineCap end_cap =
new CustomLineCap(null, end_path))
{
// Make a pen that uses the custom caps.
using (Pen the_pen = new Pen(Color.Blue, 5))
{
the_pen.CustomStartCap = start_cap;
the_pen.CustomEndCap = end_cap;
// Draw a line.
e.Graphics.DrawLine(the_pen,
40, 40, 200, 40);
// Draw a polygon.
PointF[] points = new PointF[]
{
new PointF(40, 80),
new PointF(120, 100),
new PointF(230, 70),
};
the_pen.Color = Color.Green;
e.Graphics.DrawLines(the_pen, points);
// Draw a transformed arc.
e.Graphics.ScaleTransform(3, 1);
the_pen.Color = Color.Red;
e.Graphics.DrawArc(the_pen,
20, 120, 70, 60, 180, 270);
}
}
}
}
}
}
The CustomLineCap, GraphicsPath, and Pen classes all implement the IDisposable interface, so the code creates them all in using statements. That gives the code a fairly deep nesting level but ensures that their Dispose methods are called when the objects are no longer needed.
The code first creates a GraphicsPath to define a starting line cap. It adds a semi-circular arc to the object. It then creates a CustomLineCap object, passing its constructor the GraphicsPath object for the stroke path parameter.
The code then repeats those steps to make an ending line cap.
Next the program creates a 5-pixel wide blue Pen and sets its CustomStartCap and CustomEndCap properties to the CustomLineCap objects it created. The program then draws a line, a series of 2 lines, and a scaled arc.
Notice how each of the curves drawn in the original picture start with a semi-circle and end with an arrowhead. Notice also how the end caps for the scaled red arc have also been scaled.
By using custom end caps, you can draw arrowheads and other connecting symbols on the ends of curves easily.
Download the example to experiment with it and to see additional details.
|