This is a good point to introduce some of the objects we’ll be using all the time when drawing. No matter what you draw or which drawing instrument you use, one or more of the objects discussed in this section will be required.
The Graphics Object
The Graphics object is the drawing surface — your canvas. All the controls you can draw on expose a Graphics property, which is an object, and you can retrieve it with the CreateGraphics method. Conversely, if an object doesn’t expose the CreateGraphics method, you can’t draw on its surface. It goes without saying that the PictureBox control exposes a Graphics property, but so does the TextBox control, as well as many controls you wouldn’t expect. It’s not recommended that you draw on a TextBox control, of course, unless you’re coding a peculiar application. Bear in mind that anything you draw on the TextBox control will disappear as you start typing. You must first place the text on the control and then draw on its surface — or make the control read-only. The Graphics object exposes all the methods and properties you will use to create graphics on the control. If you enter the string Me.CreateGraphics and a period, you will see a list of the members of the Graphics object in a drop-down list.
Start by declaring a variable of the Graphics type and initialize it to the Graphics object returned by the control’s CreateGraphics method:
Dim G As Graphics
G = PictureBox1.CreateGraphics
At this point, you’re ready to start drawing on the PictureBox1 control with the methods presented in the following sections. In essence, the CreateGraphics method returns the drawing surface of the control or form on which you wish to draw.
When io initialize a Graphics object?
The Graphics object is initialized to the control’s drawing surface at the moment you create it. If the form is resized at runtime, the Graphics object won’t change, and part of the drawing surface might not be available for drawing. If you create a Graphics object to represent a form in the form’s Load event handler and the form is resized at runtime, the drawing methods you apply to the Graphics object will take effect in part of the form. The most appropriate event for initializing the Graphics object and inserting the painting code is the form’s Paint event. This event is fired when the form must be redrawn when the form is uncovered or resized. Insert your drawing code there and create a Graphics object in the Paint event handler. Then draw on the Graphics object and release it when you’re done.
The Graphics object exposes the following basic properties, in addition to the drawing methods discussed in the following sections.
DpiX, DpiY – These two properties return the horizontal and vertical resolutions of the drawing surface, respectively. Resolution is expressed in pixels per inch (or dots per inch, if the drawing surface is your printer). On an average monitor, these two properties return a resolution of 96 dots per inch (dpi).
PageUnit – This property determines the units in which you want to express the coordinates on the Graphics object; its value can be a member of the GraphicsUnit enumeration (Table 14.1). If you set the PageUnit property to World, you must also set the PageScale property to a scaling factor that will be used to convert world units to pixels.
Table 14.1 – The GraphicsUnit Enumeration
|Display||The unit is 1/75 of an inch.|
|Document||The unit is 1/300 of an inch.|
|Inch||The unit is 1 inch.|
|Millimeter||The unit is 1 millimeter.|
|Pixel||The unit is 1 pixel (the default value).|
|Point||The unit is a printer’s point (1/72 of an inch).|
|World||The developer specifies the unit to be used.|
TextRenderingHint – This property specifies how the Graphics object will render text; its value is one of the members of the TextRenderingHint enumeration: AntiAlias, AntiAliasGrid- Fit, ClearTypeGridFit, SingleBitPerPixel, SingleBitPerPixelGridFit, and SystemDefault.
SmoothingMode – This property is similar to the TextRenderingHint, but it applies to shapes drawn with the Graphics object’s drawing methods. Its value is one of the members of the SmoothingMode enumeration: AntiAlias, Default, HighQuality, HighSpeed, Invalid, and None.
Figure 14.4 shows the effect of the TextRenderingHint property on text. The anti-aliased text looks much better on the monitor, because anti-aliased text is smoother. The edges of the characters contain shades between the drawing and background colors. The ClearType setting has no effect on Cathode Ray Tube (CRT) monitors. You can see the difference only when you render text on Liquid Crystal Display (LCD) monitors, such as flat-panel or notebook monitors. Text in ClearType style looks best when rendered black on a white background. You won’t be able to see the differences among the various settings on the printed image, but you can open the TextRenderingHint project (download here), which I used to create the figure, and examine how the TextRenderingHint property affects the rendering of the text. You can also capture the form by pressing Alt+PrtSc, paste it into Paint or your favorite image-processing application, and zoom into the details of the various characters.
Many of the drawing methods of the Graphics object use some helper classes, such as the Point class that’s used to specify coordinates, the Color class that’s used to specify colors, and so on. I’ll go quickly through these classes, and then I’ll discuss the drawing methods in detail.
The Point Class
The Point class represents a point on the drawing surface and is expressed as a pair of (x, y) coordinates. The x-coordinate is its horizontal distance from the origin, and the y-coordinate is its vertical distance from the origin. The origin is the point with coordinates (0, 0), and this is the top-left corner of the drawing surface.
The constructor of the Point class is the following, where X and Y are the point’s horizontal and vertical distances from the origin:
Dim P1 As New Point(X, Y)
You can also set the X and Y properties of the P1 variable. As you will see later, coordinates can be specified as single numbers, not integers (if you choose to use a coordinate system other than pixels). In this case, use the PointF class, which is identical to the Point class except that its coordinates are nonintegers. (F stands for floating-point, and floating-point numbers are represented by the Single or Double data type.)
The Rectangle Class
Another class that is often used in drawing is the Rectangle class. The Rectangle object is used to specify areas on the drawing surface. Its constructor accepts as arguments the coordinates of the rectangle’s top-left corner and its dimensions:
Dim box As Rectangle
box = New Rectangle(X, Y, width, height)
The following statement creates a rectangle whose top-left corner is 1 pixel to the right and 1 pixel down from the origin, and its dimensions are 100 by 20 pixels:
box = New Rectangle(1, 1, 100, 20)
The box variable represents a rectangle, but it doesn’t generate any output on the monitor. If you want to draw the rectangle, you can pass it as argument to the DrawRectangle or FillRectangle method, depending on whether you want to draw the outline of the rectangle or a filled rectangle.
Another form of the Rectangle constructor uses a Point and a Size object to specify the location and dimensions of the rectangle:
box = New Rectangle(point, size)
The point argument is a Point object that represents the coordinates of the rectangle’s upperleft corner. To create the same Rectangle object as in the preceding example with this form of the constructor, use the following statement:
Dim P As New Point(1, 1)
Dim S As New Size(100, 20)
box = New Rectangle(P, S)
Both sets of statements create a rectangle that extends from point (1, 1) to the point (1 + 100, 1 + 20) or (101, 21), in the same manner as the ones shown in Figure 14.5. Alternatively, you can declare a Rectangle object and then set its X, Y, Width, and Height properties.
The Size Class
The Size class represents the dimensions of a rectangle; it’s similar to a Rectangle object, but it doesn’t have an origin, just dimensions. To create a new Size object, use the following constructor:
Dim S1 As New Size(100, 400)
If you want to specify coordinates as fractional numbers, use the SizeF class, which is identical to the Size class except that its dimensions are nonintegers.
The Color Class
The Color class represents colors, and there are many ways to specify a color. We’ll discuss the Color class in more detail in Chapter 19, “Manipulating Images and Bitmaps.” In the meantime, you can specify colors by name. Declare a variable of the Color type and initialize it to one of the named colors exposed as properties of the Color class:
Dim myColor As Color
myColor = Color.Azure
The 128 color names of the Color class will appear in the IntelliSense box as soon as you enter the period following the keyword Color. You can also use the FromARGB method, which creates a new color from its basic color components (the Red, Green, and Blue components). For more information on specifying colors with this method, see the section called “Specifying Colors” in Chapter “Manipulating Images and Bitmaps”.
The Font Class
The Font class represents fonts, which are used when rendering strings via the DrawString method. To specify a font, you must create a new Font object; set its family name, size, and style; and then pass it as argument to the DrawString method. Alternatively, you can prompt the user for a font via the Font common dialog box and use the object returned by the dialog box’s Font property as an argument with the DrawString method. To create a new Font object, use a statement like the following:
Dim drawFont As New Font(”Verdana”, 12, FontStyle.Bold)
The Font constructor has 13 forms in all. Two of the simpler forms of the constructor, which allow you to specify the size and the style of the font, are shown in the following code lines, where size is an integer and style is a member of the FontStyle enumeration (Bold, Italic, Regular, Strikeout, and Underline):
Dim drawFont As New Font(name, size)
Dim drawFont As New Font(name, size, style)
To specify multiple styles, combine them with the OR operator:
FontStyle.Bold Or FontStyle.Italic
You can also initialize a Font variable to an existing font. The following statement creates a Font object and initializes it to the current font of the form:
Dim textFont As New Font
textFont = Me.Font
The Pen Class
The Pen class represents virtual pens, which you use to draw on the Graphics object’s surface. To construct a new Pen object, you must specify the pen’s color and width in pixels. The following statements declare three Pen objects with the same color and different widths:
Dim thinPen, mediumPem, thickPen As Pen
thinPen = New Pen(Color.Black, 1)
mediumPen = New Pen(Color.Black, 3)
thickPen = New Pen(Color.Black, 5)
If you omit the second argument, a pen with a width of a single pixel will be created by default. Another form of the Pen object’s constructor allows you to specify a brush instead of a color, as follows, where brush is a Brush object (discussed later in this chapter):
Dim patternPen as Pen
patternPen = New Pen(brush, width)
The quickest method of creating a new Pen object is to use the built-in Pens collection, which creates a Pen with a width of 1 pixel and the color you specify. The following statement can appear anywhere a Pen object is required and will draw shapes in blue color:
The Pen object exposes these properties:
Alignment – Determines the alignment of the Pen, and its value is one of the members of the PenAlignment enumeration: Center or Inset. When set to Center, the width of the pen is centered on the outline (half the width is inside the shape, and half is outside). When set to Inset, the entire width of the pen is inside the shape. The default value of this property isPenAlignment.Center.
LineJoin – Determines how two consecutive line segments will be joined. Its value is one of the members of the LineJoin enumeration: Bevel, Miter, MiterClipped, and Round. StartCap, EndCap Determines the caps at the two ends of a line segment, respectively. Their value is one of the members of the LineCap enumeration: Round, Square, Flat, Diamond, and so on.
DashCap – Determines the caps to be used at the beginning and end of a dashed line. Its value is one of the members of the DashCap enumeration: Flat, Round, and Triangle.
DashStyle – Determines the style of the dashed lines drawn with the specific Pen. Its value is one of the members of the DashStyle enumeration (Solid, Dash, DashDot, DashDotDot, Dot, and Custom).
PenType – Determines the style of the Pen; its value is one of the members of the PenType enumeration: HatchFilled, LinearGradient, PathGradient, SolidColor, and TextureFill.
The Brush Class
The Brush class represents the instrument for filling shapes; you can create brushes that fill with a solid color, a pattern, or a bitmap. In reality, there’s no Brush object. The Brush class is actually an abstract class that is inherited by all the classes that implement a brush, but you can’t declare a variable of the Brush type in your code. The brush objects are shown in Table 14.2.
Table 14.2 – Brush Styles
|SolidBrush||Fills shapes with a solid color|
|HatchBrush||Fills shapes with a hatched pattern|
|LinearGradientBrush||Fills shapes with a linear gradient|
|PathGradientBrush||Fills shapes with a gradient that has one starting color and many ending colors|
|TextureBrush||Fills shapes with a bitmap|
To fill a shape with a solid color, you must create a SolidBrush object with the following constructor, where brushColor is a color value, specified with the help of the Color object: Dim sBrush As SolidBrush
sBrush = New SolidBrush(brushColor)
Every filled object you draw with the sBrush object will be filled with the color of the brush.
To fill a shape with a hatch pattern, you must create a HatchBrush object with the following constructor:
Dim hBrush As HatchBrush
HBrush = New HatchBrush(hatchStyle, hatchColor, backColor)
The first argument is the style of the hatch, and it can have one of the values shown in Table 14.3 and in the following illustration. The HatchStyle enumeration has 54 members, so Table 14.3 shows only a few common patterns. You can fill shapes with plaid, spheres, waves, and a lot more patterns that aren’t listed here, but you will see their names in the IntelliSense box. The other two arguments are the colors to be used in the hatch. The hatch is a pattern of lines drawn on a background, and the two color arguments are the color of the hatch lines and the color of the background on which the hatch is drawn.
Table 14.3 – The HatchStyle Enumeration
|BackwardDiagonal||Diagonal lines from top-right to bottom-left|
|Cross||Vertical and horizontal crossing lines|
|DiagonalCross||Diagonally crossing lines|
|ForwardDiagonal||Diagonal lines from top-left to bottom-right|
A gradient brush fills a shape with a specified gradient. The LinearGradientBrush fills a shape with a linear gradient, and the PathGradientBrush fills a shape with a gradient that has one starting color and one or more ending colors. Gradient brushes are discussed in detail in the section titled “Gradients,” later in this chapter.
In addition to solid and hatched shapes, you can fill a shape with a texture by using a TextureBrush object. The texture is a bitmap that is tiled as needed to fill the shape. Textured brushes are used to create rather fancy graphics, and we won’t explore them in this tutorial.
The Path Class
The Path class represents shapes made up of various drawing entities, such as lines, rectangles, and curves. You can combine as many of these drawing entities as you’d like and build a new entity, which is called a path. Paths are usually closed and filled with a color, a gradient, or a bitmap. You can create a path in several ways. The simplest method is to create a new Path object and then use one of the following methods to append the appropriate shape to the path:
These methods add to the path the same shapes you can draw on the Graphics object with the methods discussed in the following section. There’s even an AddPath method, which adds an existing path to the current one. The syntax of the various methods that add shapes to a path is identical to the corresponding methods that draw. We simply omit the first argument (the Pen object) because all the shapes that make up a path will be rendered with the same pen. The following method draws an ellipse:
Me.CreateGraphics.DrawEllipse(mypen, 10, 30, 40, 50)
To add the same ellipse to a Path object, use the following statement:
Dim myPath As New Path
myPath.AddEllipse(10, 30, 40, 50)
To display the path, call the DrawPath method, passing a Pen and Path object as arguments:
Why combine shapes into paths instead of drawing individual shapes? After the shape has been defined, you can draw multiple instances of it, draw the same path with a different pen, or fill the path’s interior with a gradient. Paths are also used to create the ultimate type of gradient, the PathGradient (as you will see in the section called “Path Gradients,” later in this chapter).