Drawing Basics
Most computer-based drawing is done on a two-dimensional plane using a basic set of objects. These objects are like building blocks. In the hands of a competent craftsman, they can be manipulated to create interesting effects and complex shapes. But before we can build the skyscraper, we must first set the basic foundation.
Understanding Windows Coordinate Systems
The default coordinate system in Windows has the origin (0, 0) in the upper-left corner of the drawing surface. The x-axis extends to the right, while the y-axis extends downward. The pixel is the unit of measurement in the default coordinate system. To draw to a surface, you specify what pixels you want your monitor to "turn on" to create the graphic. For instance, a line can be defined by joining the pixels between a start and an end coordinate. Figure 9.1 illustrates these concepts.
Figure 9.1 Windows default coordinate system.
The Graphics Class
The Graphics class is used to render the majority of all two-dimensional drawing in .NET. It is far and away the class you will most often use to execute basic drawing tasks. The class provides methods for drawing all the basic 2D shapes, including: lines, rectangles, ellipses, polygons, arcs, cardinal splines, and Bèzier splines. For the most part, if you need to draw a shape, there will be an associated method of the Graphics class. For example, the DrawLine method is used to draw a line and the DrawRectangle method is used for a rectangle. In fact, you can often draw several graphic elements with a single method call. To do so, you simply use the plural version of a given drawing method, such as DrawLines or DrawRectangles. All of these methods (and the Graphics class itself) are covered in-depth throughout the rest of this chapter.
Pens
The companion to the Graphics class is the Pen class. In fact, in order to draw nearly anything, you'll need at least a Graphics and a Pen instance. The Pen class is used to define how the outlines of shapes are rendered to the surface. Similar to a real pen, the Pen class defines a width color that will be used to do the drawing. Additionally, you can create a Pen based on a Brush instance. This allows you to draw with more stylized lines. Table 9.2 demonstrates the various constructors that are available to you when creating new Pen instances.
Table 9.2 Pen Constructors
Constructor |
Description |
New Pen(Color, Single) |
Creates a Pen object based on a color (Color structure) and a width (Single) in pixels. |
New Pen(Color) |
Creates a Pen object based on a color defined by the Color structure. Sets the pen's width to the default of 1 pixel. |
New Pen(Brush, Single) |
Creates a Pen object using a valid class derived from the Brush base class. The pen's width (Single) is defined in pixels. |
New Pen(Brush) |
Creates a Pen object based on a valid Brush object. Sets the pen's width to the default of 1.0 pixels. |
The Color parameter, used in the Pen constructor, is defined by an instance of the Color structure. The Color structure represents an ARGB (Alpha, Red, Green, and Blue) color. Most colors come predefined as properties of the Color structure for easy use. For example, Color.Red indicates the ARGB equivalent of red. There are a wide variety of predefined colors, everything from LawnGreen to Tomato to Transparent. Additionally, you can call methods of the Color structure to create custom colors or return the brightness, saturation, and hue of a given color.
Lines
So far, we've talked about pens and drawing methods but have yet to render anything to the screen. Now we'll use a Pen object to draw a line onto a form. This may not seem exciting, but it provides a foundation.
A line is a set of pixels linked by a start and end point. Line attributes are defined by the Pen object with which they are drawn. Of course, as pens can vary in width and color, so to can lines. To draw a line, we use the DrawLine method of the Graphics class. This method is overloaded; it defines a number of ways you can pass it parameters. For instance, you can pass it a Pen object and two Point structures between which GDI+ will draw the line. A Point structure stores the x and y coordinates of a point on a 2D plane.
The following code uses the DrawLine method and a Pen instance to draw a blue line onto a form. You can test this code, create a new form-based application, add a button to it, and add the code in the listing to the button's click event.
'local scope Dim myGraphics As Graphics Dim myPen As Pen 'return the current form as a drawing surface myGraphics = Graphics.FromHwnd(hwnd:=ActiveForm().Handle) 'instantiate a new pen object using the color structure myPen = New Pen(color:=Color.Blue, Width:=4) 'draw the line on the form using the pen object myGraphics.DrawLine(pen:=myPen, x1:=1, y1:=1, x2:=25, y2:=50)
Note that before we could draw anything to the screen, we needed to return a valid drawing surface. To do so, we created an instance of the Graphics class. This provides us an object on which to draw. The constructor accepts a Windows handle as its parameter. We pass it the active Windows handle. This sets up the Graphics object to use the active form as its target for drawing our line.
Next, a Pen instance is created. We pass its constructor a valid color and width. Finally, the DrawLine method of the Graphics object is called to render the line onto the form. The version of the DrawLine method we used requires a Pen instance and a set of start and end coordinates. These coordinates are simply passed in order as two points defined as (x1, y1) and (x2, y2). The method connects the two coordinate points with a blue line based on our Pen object.
Dashes and Caps
What if you want to add an arrow to the end of your line? Or maybe, you need a dotted line to get your point across. In addition to defining color and width, the Pen class is used to create dashed lines and to attach start and end line caps. Line caps can be as simple as an arrowhead or as complex as a custom-defined cap. Table 9.3 lists properties of the Pen class that are specific to dashes and caps.
Table 9.3 Pen Class Dash and Cap Properties
Property |
Description |
CustomStartCap |
The CustomStartCap property is used to set or get a custom-defined line cap. The CustomStartCap property defines the cap at a line's start. The property is of type CustomLineCap. |
CustomEndCap |
The CustomEndCap property is used to set or get a custom-defined line cap. The CustomEndCap property defines the cap at a line's end. The property is of type CustomLineCap. |
DashCap |
The DashCap property is used to set or get the style used for the start or end caps of dashed lines. |
DashOffset |
The DashOffset property is used to set or get the distance between the start of a line and the start of the dash pattern. |
DashPattern |
The DashPattern property sets or gets an array of integers that indicates the distances between dashes in dash-patterned lines. |
DashStyle |
The DashStyle property sets or gets the style used for dashing a line. The property is of the type DashStyle enumeration. DashStyle enumeration members include the following: Dash, DashDot, DashDotDot, Dot, Solid. |
EndCap |
The EndCap property sets or gets the LineCap object used to define the end of the line. EndCap is of the type LineCap. The LineCap enumeration includes the following members: AnchorMask, ArrowAnchor, Custom, DiamondAnchor, Flat, NoAnchor, Round, RoundAnchor, Square, SquareAnchor, and Triangle. |
StartCap |
The StartCap property sets or gets the LineCap object used to define the start of the line. StartCap is of the type LineCap. The LineCap enumeration includes the following members: AnchorMask, ArrowAnchor, Custom, DiamondAnchor, Flat, NoAnchor, Round, RoundAnchor, Square, SquareAnchor, and Triangle. |
The following code demonstrates setting the styles and cap properties of a Pen object. The code first creates a Pen object of the color blue. It then sets the EndCap property to an arrow using the LineCap enumeration. Last, it indicates the line's DashStyle to be a dash followed by a dot (DashDot).
'dimension a local variable of type Pen Dim myPen As Pen 'instantiate a Pen using the color structure and width constructor myPen = New Pen(color:=Color.Blue, Width:=5) 'set the Pen's end cap to be of type arrow myPen.EndCap = Drawing.Drawing2D.LineCap.ArrowAnchor 'set the Pen's dash style to be a dash followed by a dot myPen.DashStyle = Drawing.Drawing2D.DashStyle.DashDot
Joins
Suppose we have multiple lines that are joined to indicate a shape or routing direction through a diagram. The point at which two lines are joined can be rendered with three distinct styles. The Pen class defines how lines are joined. To do so, it provides the LineJoin property. This property is of the type LineJoin enumeration whose members include those listed in Table 9.4.
Table 9.4 LineJoin Enumeration Members
Member |
Example |
Description |
Bevel |
The Bevel member indicates a beveled join between the lines. |
|
Miter |
The Miter member specifies an angled join. |
|
Round |
The Round member creates a smooth and rounded join. |
To join lines, you must add each line to a Path object (discussed later in the chapter). The path is drawn to the surface using one Pen instance. Intersecting lines are then joined based on the LineJoin property of the given Pen instance. The following snippet illustrates this with code.
'local scope Dim myPath As New System.Drawing.Drawing2D.GraphicsPath() Dim myGraphics As Graphics Dim myPen As New Pen(color:=Color.Blue, Width:=8) 'return the current form as a drawing surface myGraphics = Graphics.FromHwnd(hwnd:=ActiveForm().Handle) 'add 2 intersecting lines to a path myPath.AddLine(10, 10, 50, 10) myPath.AddLine(50, 10, 50, 50) 'set the line join property myPen.LineJoin = Drawing.Drawing2D.LineJoin.Miter 'draw the line to the form myGraphics.DrawPath(pen:=myPen, path:=myPath)
Curves
A curve is an array of points defining the perimeter of a conic section. Curves can be used for such things as connecting points on a graph or drawing a handlebar mustache.
There are two types of curves in the .NET library: cardinal splines and Bèzier splines. There are also a number of methods of the Graphics class that can be used to draw curves. Table 9.5 lists these methods. For our discussion, we will focus on the DrawCurve and DrawBezierCurve methods.
Table 9.5 Graphics Class Curve Drawing Methods
Method |
Description |
DrawCurve |
The DrawCurve method connects an array of points using a curved line. |
DrawClosedCurve |
The DrawClosedCurve method draws a closed curve using an array of points. A closed curve ensures that the shape is closed. For instance, if you drew a curve between three points, the method would close the curve by connecting the third point with the first. |
DrawBezier |
The DrawBezier method is used to draw a Bèzier curve. |
DrawArc |
The DrawArc method draws an arc from a specified ellipse. |
Cardinal Splines
A cardinal spline is an array of points through which a line smoothly passes. The curve or bend of the line is defined by a tension parameter. The curve's tension indicates how tightly the curve bends, the lower the tension on a given curve, the flatter (straighter) the line. A curve with a tension of zero (0), for instance, is equivalent to drawing a straight line between points.
To create a cardinal spline, you use the DrawCurve method. This method allows us to control the curve's tension and define the number of points in a given curve. The method is overloaded, and as such, provides a number of ways to display a curve. In the following example, we create a blue curve that passes through three points. Notice that we did not specify the tension. When left blank, the method uses the default tension of 0.5.
'declare local variables Dim myGraphics As Graphics Dim myPen As Pen Dim myPoints(2) As Point 'create a 3-item array of point structures myPoints(0) = New Point(100, 75) myPoints(1) = New Point(125, 50) myPoints(2) = New Point(150, 75) 'return the current form as a drawing surface myGraphics = Graphics.FromHwnd(hwnd:=ActiveForm().Handle) 'instantiate a new pen object using the color structure myPen = New Pen(color:=Color.Blue, Width:=2) 'draw curve between the 3 points defined in the array myGraphics.DrawCurve(pen:=myPen, points:=myPoints)
Figure 9.2 helps illustrate the concept of curve tension. The innermost line is drawn with a tension setting of zero. Each successive line increases the tension by .5 until we reach 2.0.
Figure 9.2 Curve tension.
Bèzier Splines
Bèzier splines can be used to create a wide variety of shapes. Fonts, for instance, often use Bèzier splines for outlining characters. Four points define a Bèzier spline: a start and end point and two control points. The curve is drawn between the start and end point. The control points influence how the curve flows between the points. As the curve moves from point to point, it is "pulled" toward the nearest control point.
Consider the following code:
'declare local variables Dim myGraphics As Graphics Dim myPen As Pen Dim myPoints(3) As Point 'create a 4-item array of point structures myPoints(0) = New Point(100, 75) myPoints(1) = New Point(125, 50) myPoints(2) = New Point(150, 75) myPoints(3) = New Point(175, 50) 'return the current form as a drawing surface myGraphics = Graphics.FromHwnd(ActiveForm().Handle) 'instantiate a new pen object using the color structure myPen = New Pen(Color.Blue, 2) 'draw bezier using the points defined in the array myGraphics.DrawBezier(pen:=myPen, pt1:=myPoints(0), pt2:=myPoints(1), _ pt3:=myPoints(2), pt4:=myPoints(3))
In the preceding example, we created a Bézier curve using the DrawBezier method. First, we defined a set of four points. The first point (100, 75) is the starting point. The next two points, (125, 50) and (150, 75) act as the control points. The curve ends at the point (175, 50).
Suggestions for Further Exploration
For a complete listing of colors available through the Color structure, check out MSDN: Visual Studio .NET/.NET Framework Class Library/System.Drawing/Color Structure/Properties.
Use the curve code examples presented in this section to experiment with the DrawArc and DrawClosedCurve methods.
Use DrawBeziers method to create a series of Bèzier curves.