2-D Graphics Programming with GDI+

The Windows operating system has always included support for drawing two-dimensional graphics. This support is known as the Graphics Device Interface (GDI) library. The GDI library is now easier to use and provides additional features. The new capabilities are known collectively as GDI+. GDI+ features are exposed in the .NET Framework through classes in the System.Drawing, System.Drawing.Drawing2D, System.Drawing.Imaging, and System.Drawing.Text namespaces. This section discusses some of those capabilities.

The Graphics Class

Objects of type Graphics (defined in the System.Drawing namespace) represent two-dimensional surfaces on which to draw. A Graphics object must be obtained before any drawing can be done. A common way to obtain a Graphics object is to override the OnPaint method of a form or user control, as shown in the following code fragment:

Public Class MyControl
   Inherits UserControl

   Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
      e.Graphics.FillEllipse(New SolidBrush(Me.ForeColor), _
         Me.ClientRectangle)
   End Sub

   Public Sub New(  )
      Me.ResizeRedraw = True
   End Sub

End Class

The single argument passed to the OnPaint method, e, is of type PaintEventArgs. This class has a property called Graphics, which holds a reference to the Graphics object to be used for drawing on the user control or form. The PaintEventArgs class is defined in the System.Windows.Forms namespace. It has two properties:

ClipRectangle

Defines the area that needs to be drawn. Drawing done outside the limits of the clip rectangle will not be displayed. The coordinates of the rectangle are relative to the client rectangle of the user control or form.

The syntax of the ClipRectangle property is:

Public ReadOnly Property ClipRectangle(  ) As System.Drawing.Rectangle
Graphics

Defines the graphics surface on which to draw. The syntax of the Graphics property is:

Public ReadOnly Property Graphics(  ) As System.Drawing.Graphics

The following list shows some of the Graphics class’s many methods that are available for drawing various lines and shapes, and Example 5-7 in Chapter 5 gives an example of drawing a filled ellipse. This list is just to get you started; it is beyond the scope of this book to document the syntax of each of these methods.

DrawArc

Draws an arc (that is, a portion of an ellipse).

DrawBezier

Draws a Bezier curve.

DrawBeziers

Draws a series of Bezier curves.

DrawClosedCurve

Is the same as the DrawCurve method (see the next item in this list), except that the last point in the curve is connected back to the first point.

DrawCurve

Draws a smooth, curved figure that passes through a given array of points.

DrawEllipse

Draws an ellipse.

DrawIcon

Draws an icon. Icons are represented by objects of type Icon (defined in the System.Drawing namespace). The Icon class defines various methods for loading icons.

DrawIconUnstretched

Is the same as the DrawIcon method, but does not stretch the icon to fit the clipping rectangle.

DrawImage

Draws an image. Images are represented by objects of type Image (defined in the System.Drawing namespace). The Image class defines various methods for loading images in standard formats, such as bitmaps and JPEGs.

DrawImageUnscaled

Is the same as DrawImage, except that the DrawImageUnscaled method ignores any width and height parameters passed to it.

DrawLine

Draws a line.

DrawLines

Draws a series of lines.

DrawPath

Draws a series of lines and curves that are defined by a GraphicsPath object. The GraphicsPath class is beyond the scope of this book.

DrawPie

Draws a pie section.

DrawPolygon

Draws lines to connect a series of points.

DrawRectangle

Draws a rectangle.

DrawRectangles

Draws a series of rectangles.

DrawString

Draws text.

FillClosedCurve

Draws a filled, closed curve.

FillEllipse

Draws a filled ellipse.

FillPath

Draws a filled figure whose shape is given by a GraphicsPath object. The GraphicsPath class is beyond the scope of this book.

FillPie

Draws a filled pie section.

FillPolygon

Draws a filled polygon (see the DrawPolygon method earlier in this list).

FillRectangle

Draws a filled rectangle.

FillRectangles

Draws a series of filled rectangles.

FillRegion

Draws a filled figure whose shape is given by a Region object.

The Pen Class

Pen objects hold the settings used when drawing lines. All of the Graphics class’s Draw...methods (DrawArc, DrawBezier, etc.) require that the caller supply a Pen object. The supplied Pen object determines the properties of the line used for drawing (for example, its color, width, etc.). Example 4-9 shows an OnPaint method that can be used to draw an ellipse on a user control or a form. It is similar to the code in Example 5-6 in Chapter 5, but displays the ellipse a little smaller, and with only a border. The resulting display is shown in Figure 4-9.

Example 4-9. Drawing an ellipse on a form

Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
   Dim pn As New Pen(Me.ForeColor)
   Dim rect As Rectangle

   rect.X = Me.ClientRectangle.X + (Me.ClientRectangle.Width \ 4)
   rect.Y = Me.ClientRectangle.Y + (Me.ClientRectangle.Height \ 4)
   rect.Width = Me.ClientRectangle.Width \ 2
   rect.Height = Me.ClientRectangle.Height \ 2

   e.Graphics.DrawEllipse(pn, rect)
   pn.Dispose(  )
End Sub
The ellipse drawn by the code in Example 4-9

Figure 4-9. The ellipse drawn by the code in Example 4-9

In Example 4-9, the Graphics class’s DrawEllipse method is passed a Pen object, which determines the appearance of the line used for drawing the ellipse, and a rectangle, which defines the shape of the ellipse. The Pen class has four constructors. The constructor used in Example 4-9 takes a parameter of type Color (defined in System.Drawing). The color passed to the Pen class constructor in Example 4-9 is the foreground color of the form (Me.ForeColor). This is a nice touch ensuring that the ellipse will be drawn using whatever color is set as the foreground color of the form on which the ellipse is drawn. See Section 4.6.4 later in this chapter for information on choosing and manipulating colors. Finally, note this line in Example 4-9:

pn.Dispose(  )

By convention, objects that allocate scarce resources expose a Dispose method to allow the object client to tell the object to release its resources. When using any object that exposes a Dispose method (as the Pen object does), the Dispose method must be called when the client code is finished using the object. If the Dispose method isn’t called (or if it isn’t implemented), resources will be held longer than necessary, which may in turn result in resources being unavailable for other code that needs them.

The .NET Framework provides a number of predefined pens through the properties of the Pens and SystemPens classes (defined in the System.Drawing namespace). For example, the Blue property of the Pens class returns a Pen object whose color is set to Color.Blue. Thus, the following line of code draws a blue ellipse:

e.Graphics.DrawEllipse(Pens.Blue, rect)

Similarly, the SystemPens class’s WindowText property returns a Pen object whose color is set to the system’s window text color. Using the standard pens provided by the Pens and SystemPens classes can be more efficient than instantiating new Pen objects. However, their properties (such as line width) cannot be altered.

See Table 4-1, later in this chapter, for the list of Pen objects available through the Pens class. See Section 4.6.4.1 in Section 4.6.4 later in this chapter for the list of Pen objects available through the SystemPens class.

When working with a user-instantiated pen, you can modify the line that is drawn by setting properties of the Pen object. The code in Example 4-10 sets the Pen object’s Width property to widen the outline of the ellipse. The lines of code that differ from Example 4-9 are shown in bold. The resulting display is shown in Figure 4-10.

Example 4-10. Setting Pen properties

Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
   Dim pn As New Pen(Me.ForeColor)
   pn.Width = 10
   pn.DashStyle = Drawing.Drawing2D.DashStyle.Dash
   Dim rect As Rectangle

   rect.X = Me.ClientRectangle.X + (Me.ClientRectangle.Width \ 4)
   rect.Y = Me.ClientRectangle.Y + (Me.ClientRectangle.Height \ 4)
   rect.Width = Me.ClientRectangle.Width \ 2
   rect.Height = Me.ClientRectangle.Height \ 2

   e.Graphics.DrawEllipse(pn, rect) 
   pn.Dispose(  )
End Sub
The ellipse drawn by the code in Example 4-10

Figure 4-10. The ellipse drawn by the code in Example 4-10

Example 4-10 sets the Pen object’s Width and DashStyle properties to attain the effect shown in Figure 4-10. The Width property is a value of type Single that determines the width of lines drawn with this pen. The default is 1. The unit of measurement is determined by the PageUnit property of the Graphics object in which the lines are drawn. The PageUnit property is of the enumeration type GraphicsUnit (defined in the System.Drawing namespace). The values of GraphicsUnit that are appropriate for assignment to the PageUnit property are:

Display

Units are specified in 1/75 of an inch.

Document

Units are specified in 1/300 of an inch.

Inch

Units are specified in inches.

Millimeter

Units are specified in millimeters.

Pixel

Units are specified in pixels.

Point

Units are specified in points (1/72 of an inch).

The DashStyle property of the Pen object determines the whether the line is solid or dashed, as well as the style of the dash. The DashStyle property is of the enumeration type DashStyle (defined in the System.Drawing.Drawing2D namespace), which defines the following values:

Custom

Specifies a programmer-defined dash style. If this value is used, other properties of the Pen object control the exact appearance of the dashes in the line. Creating custom dash styles is not discussed in this book.

Dash

Specifies a dashed line.

DashDot

Specifies a line consisting of alternating dashes and dots.

DashDotDot

Specifies a line consisting of alternating dashes and two dots.

Dot

Specifies a dotted line.

Solid

Specifies a solid line.

The standard dash styles are shown in Figure 4-11.

The standard DashStyle values

Figure 4-11. The standard DashStyle values

The Brush Class

Brush objects hold the settings used when filling graphics areas. All of the Graphics class’s Fill...methods (FillClosedCurve, FillEllipse, etc.) require that the caller supply a Brush object. The supplied Brush object determines how the interior of the figure will be painted. Example 4-11 shows an OnPaint method that can be used to draw an ellipse on a user control or a form. It is similar to Example 4-9, but draws a filled ellipse rather than an outline. The lines that differ from Example 4-9 are shown in bold. The resulting display is shown in Figure 4-12.

Example 4-11. Drawing a filled ellipse on a form

Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)
   Dim br As New SolidBrush(Me.ForeColor)
   Dim rect As Rectangle

   rect.X = Me.ClientRectangle.X + (Me.ClientRectangle.Width \ 4)
   rect.Y = Me.ClientRectangle.Y + (Me.ClientRectangle.Height \ 4)
   rect.Width = Me.ClientRectangle.Width \ 2
   rect.Height = Me.ClientRectangle.Height \ 2
e.Graphics.FillEllipse(br, rect)
   br.Dispose(  )
End Sub
The ellipse drawn by the code in Example 4-11

Figure 4-12. The ellipse drawn by the code in Example 4-11

Note that Example 4-11 is not entirely parallel to Example 4-9. Specifically, Example 4-9 instantiated a Pen object directly, but Example 4-11 instantiates an object from a class that derives from the Brush class rather than directly from the Brush class. Objects can’t be directly instantiated from the Brush class. The classes that derive from Brush are:

HatchBrush

Fills an area with a hatch pattern. Hatch patterns are patterns of lines and spaces. The HatchBrush class’s HatchStyle property determines the exact pattern of the hatch. It is defined in the System.Drawing.Drawing2D namespace.

LinearGradientBrush

Fills an area with a gradient blend of two or more colors. It is defined in the System.Drawing.Drawing2D namespace.

PathGradientBrush

Fills the internal area of a GraphicsPath object with a gradient blend of two or more colors. It is defined in the System.Drawing.Drawing2D namespace.

SolidBrush

Fills an area with a solid color. It is defined in the System.Drawing namespace.

TextureBrush

Fills an area with an image. It is defined in the System.Drawing namespace.

The .NET Framework provides a number of predefined brushes through the properties of the Brushes and SystemBrushes classes (defined in the System.Drawing namespace). For example, the Blue property of the Brushes class returns a Brush object that fills areas with solid blue. Thus, the following line of code draws a solid blue ellipse:

e.Graphics.FillEllipse(Brushes.Blue, rect)

Similarly, the SystemBrushes class’s Window property returns a Brush object whose color is set to the background color of the system’s window client area. Using the standard brushes provided by the Brushes and SystemBrushes classes can be more efficient than instantiating new Brush objects. However, their properties cannot be altered.

See Table 4-1 for the list of Brush objects available through the Brushes class. See Section 4.6.4.1 for the list of Brush objects available through the SystemBrushes class.

The Color Structure

Colors are represented by values of type Color. The Color structure defines 141 named colors and exposes them as shared read-only properties whose values are of type Color. They serve the purpose of color constants. For example, the following code fragment sets the background color of the form frm to white:

frm.BackColor = Color.White

The color properties exposed by the Color structure have the same names as the pen properties exposed by the Pens class and the brush properties exposed by the Brushes class. The list is lengthy, so it is printed here only once, in Table 4-1.

Table 4-1. Properties common to the Color, Pens, and Brushes classes

AliceBlue

AntiqueWhite

Aqua

Aquamarine

Azure

Beige

Bisque

Black

BlanchedAlmond

Blue

BlueViolet

Brown

BurlyWood

CadetBlue

Chartreuse

Chocolate

Coral

CornflowerBlue

Cornsilk

Crimson

Cyan

DarkBlue

DarkCyan

DarkGoldenrod

DarkGray

DarkGreen

DarkKhaki

DarkMagenta

DarkOliveGreen

DarkOrange

DarkOrchid

DarkRed

DarkSalmon

DarkSeaGreen

DarkSlateBlue

DarkSlateGray

DarkTurquoise

DarkViolet

DeepPink

DeepSkyBlue

DimGray

DodgerBlue

Firebrick

FloralWhite

ForestGreen

Fuchsia

Gainsboro

GhostWhite

Gold

Goldenrod

Gray

Green

GreenYellow

Honeydew

HotPink

IndianRed

Indigo

Ivory

Khaki

Lavender

LavenderBlush

LawnGreen

LemonChiffon

LightBlue

LightCoral

LightCyan

LightGoldenrodYellow

LightGray

LightGreen

LightPink

LightSalmon

LightSeaGreen

LightSkyBlue

LightSlateGray

LightSteelBlue

LightYellow

Lime

LimeGreen

Linen

Magenta

Maroon

MediumAquamarine

MediumBlue

MediumOrchid

MediumPurple

MediumSeaGreen

MediumSlateBlue

MediumSpringGreen

MediumTurquoise

MediumVioletRed

MidnightBlue

MintCream

MistyRose

Moccasin

NavajoWhite

Navy

OldLace

Olive

OliveDrab

Orange

OrangeRed

Orchid

PaleGoldenrod

PaleGreen

PaleTurquoise

PaleVioletRed

PapayaWhip

PeachPuff

Peru

Pink

Plum

PowderBlue

Purple

Red

RosyBrown

RoyalBlue

SaddleBrown

Salmon

SandyBrown

SeaGreen

SeaShell

Sienna

Silver

SkyBlue

SlateBlue

SlateGray

Snow

SpringGreen

SteelBlue

Tan

Teal

Thistle

Tomato

Transparent

Turquoise

Violet

Wheat

White

WhiteSmoke

Yellow

YellowGreen

System colors

It is useful to discover the colors that Windows uses to draw specific window elements, such as the active window’s title bar. If the color itself is required, it can be obtained from the SystemColors class. If a pen or brush of the appropriate color is needed, the pen or brush can be obtained from the corresponding property of the Pens or Brushes class, respectively. The property names exposed by these three classes overlap and, therefore, are presented here in a single list:

ActiveBorder

The color of the filled area of the border of the active window. (Not available on the Pens class.)

ActiveCaption

The background color of the title bar of the active window. (Not available on the Pens class.)

ActiveCaptionText

The text color in the title bar of the active window.

AppWorkspace

The background color of MDI parent windows. (Not available on the Pens class.)

Control

The background color of controls.

ControlDark

The shadow color of controls (for 3-D effects).

ControlDarkDark

The very dark shadow color of controls (for 3-D effects).

ControlLight

The highlight color of controls (for 3-D effects).

ControlLightLight

The very light highlight color of controls (for 3-D effects).

ControlText

The color of text on controls.

Desktop

The color of the Windows desktop. (Not available on the Pens class.)

GrayText

The text color of disabled controls or other disabled visual elements. (Not available on the Brushes class.)

Highlight

The background color of highlighted (selected) text.

HighlightText

The text color of highlighted (selected) text.

HotTrack

The background color of a hot tracked item. Hot tracking is highlighting an item as the mouse moves over it. Windows menus use hot tracking. (Not available on the Pens class.)

InactiveBorder

The color of the filled areas of the borders of inactive windows. (Not available on the Pens class.)

InactiveCaption

The background color of the title bars of inactive windows. (Not available on the Pens class.)

InactiveCaptionText

The text color in the title bars of inactive windows. (Not available on the Brushes class.)

Info

The background color of tool tips. (Not available on the Pens class.)

InfoText

The text color of tool tips. (Not available on the Brushes class.)

Menu

The background color of menus. (Not available on the Pens class.)

MenuText

The text color of menus. (Not available on the Brushes class.)

ScrollBar

The color of scroll bars in the area not occupied by the scroll box (or thumb). (Not available on the Pens class.)

Window

The background color of the client areas of windows. (Not available on the Pens class.)

WindowFrame

The color of the frames surrounding windows. (Not available on the Brushes class.)

WindowText

The color of the text in the client areas of windows.

Note that some of these properties aren’t available on either the Pens class or the Brushes class. In such cases, it is still possible to get a Pen or Brush object of the appropriate color by instantiating a new Pen or Brush object, passing to its constructor the desired color value, like this:

Dim br As New SolidBrush(SystemColors.InfoText)

Alpha Blending

Alpha blending is a process that allows a Graphics object to appear transparent, causing Graphics objects beneath it to be seen through the object. The degree of transparency can be controlled in steps from completely transparent (invisible) to completely opaque (obscuring any objects beneath it). To draw a transparent object, instantiate Pen and Brush objects having colors whose alpha component is less than the maximum value of 255. A color’s alpha component is given by the A property of the Color structure. This property is a Byte, so it can take values from 0 (invisible) to 255 (completely opaque).

Example 4-12 shows an OnPaint method that draws text and then draws two overlapping, transparent ellipses in the same space as the text. Normally, the ellipses would obscure the text, and the second ellipse would obscure the first. In this case, however, the text can be seen through the ellipses because the ellipses are transparent, and the first ellipse can be seen through the second. The result is shown in Figure 4-13.

Example 4-12. Drawing transparent figures

Protected Overrides Sub OnPaint(ByVal e As PaintEventArgs)

   ' Determine the text to display and its font.
   Dim str As String = "Here is some text to display in a form."
   Dim fnt As New Font("Arial", 10, FontStyle.Regular, GraphicsUnit.Point)

   ' Determine the X and Y coordinates at which to draw the text so
   ' that it is centered in the window.
   Dim szf As SizeF = e.Graphics.MeasureString(str, fnt)
   Dim xText As Single = (Me.DisplayRectangle.Width - szf.Width) / 2
   Dim yText As Single = (Me.DisplayRectangle.Height - szf.Height) / 2

   ' Draw the text.
   e.Graphics.DrawString(str, fnt, Brushes.Black, xText, yText)

   ' Create a blue brush that is mostly transparent.
   Dim br As New SolidBrush(Color.FromArgb(160, Color.Blue))

   ' Determine the bounding rectangle for the first ellipse.
   Dim rect As Rectangle
   rect.X = Me.DisplayRectangle.X + (Me.DisplayRectangle.Width \ 8)
   rect.Y = Me.DisplayRectangle.Y + (Me.DisplayRectangle.Height \ 8)
   rect.Width = Me.DisplayRectangle.Width \ 2
   rect.Height = Me.DisplayRectangle.Height \ 2

   ' Draw the first ellipse.
   e.Graphics.FillEllipse(br, rect)

   ' Release the brush.
   br.Dispose(  )

   ' Create a red brush that is mostly transparent.
   br = New SolidBrush(Color.FromArgb(160, Color.Red))

   ' Determine the bounding rectangle for the second ellipse.
   rect.X += (Me.DisplayRectangle.Width \ 4)
   rect.Y += (Me.DisplayRectangle.Height \ 4)

   ' Draw the second ellipse.
   e.Graphics.FillEllipse(br, rect)

   ' Release the brush.
   br.Dispose(  )

End Sub
The display drawn by the code in Example 4-12

Figure 4-13. The display drawn by the code in Example 4-12

Antialiasing

Antialiasing is a technique for making the edges of graphics figures appear less jagged. To turn on antialiasing, set the Graphics object’s SmoothingMode property to SmoothingMode.AntiAlias. (SmoothingMode is an enumeration defined in the System.Drawing.Drawing2D namespace.) Compare the arcs shown in Figure 4-14. Both arcs were drawn by calling the DrawArc method of the Graphics class, but the arc on the left was drawn with the SmoothingMode property set to SmoothingMode.None (the default), and the arc on the right was drawn with the SmoothingMode property set to SmoothingMode.AntiAlias. Figure 4-15 shows a close-up comparison view of the upper portion of both arcs.

Nonantialiased versus antialiased arcs

Figure 4-14. Nonantialiased versus antialiased arcs

Close-up view of nonantialiased and antialiased arcs

Figure 4-15. Close-up view of nonantialiased and antialiased arcs

As Figure 4-15 shows, antialiasing appears to improve pixel resolution by using gradient shades of the color being rendered and of the background color (in this case, black and white, respectively).

The downside to antialiasing is that it takes more time to render.

Get Programming Visual Basic .NET now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.