GDI Basics


Visual Basic .NET Unleashed
By Paul Kimmel
Table of Contents
Chapter 17.  Programming with GDI+

GDI+ Basics

GDI+ is supported in the CLR by the System.Drawing namespace and subordinate elements. GDI+ includes classes representing the DC, brushes, pens, fonts, and basic shape-drawing capabilities, as well as support for imaging and rendering graphics on devices in addition to Windows Forms.

Support for drawing in Windows Forms is supported at the API level. GDI+ is a framework within the CLR that provides us with refined metaphors that make what we have always been able to do easier.

Using Graphics Objects

The Graphics class is the metaphor that represents the API device context. Methods , fields, and properties in the Graphics class conceal working with handles and HRESULT return values.

Generally, you have access to a Graphics object when you explicitly call CreateGraphics or as a member of the PaintEventArgs Paint event argument. Because the Graphics object is stateless, you should not cache a Graphics object; you should request a Graphics object each time you need to perform a graphics operation. Additionally, the Graphics object represents a valuable system resource; thus, following advice earlier in this book, we should call Dispose when we are done with any Graphics object.


Any object you are going to create and destroy should be wrapped in a resource protection block using the Try..Finally idiom introduced in VB .NET.

As with any resource, if you want to ensure that the Dispose method is called, wrap the call to CreateGraphics and Dispose in a resource protection block.

Creating a Graphics Object

Classes in Visual Basic .NET support constructors instead of the Finalize event called internally. The availability of constructors allows additional idioms to be employed. One such idiom is to devise a factory method and make the constructor protected or private. The Graphics class uses this technique, with the end result being that you might only create instances via the factory method.

The reason for employing a factory method is usually to ensure that a construction and initialization order occurs reliably. Graphics objects are associated by the handle of a device context (hDC) for a specific control.

As with any other object, when you have an instance, you can invoke any of the instance methods or properties. To create an instance of a Graphics object, call Control. CreateGraphics where Control is the instance of the control whose DC, or canvas, you want to draw on. The following code demonstrates how to get an instance of a Graphics object for a Form:

 Dim G As Graphics = CreateGraphics() 


You can create Graphics objects from static methods of the Graphics class by passing the HWnd, or windows handle, using the Handle property. There are several other shared Graphics methods that allow you to create instances, but the constructor is Private.

Unless there are exigent circumstances, you should use the Control. CreateGraphics method.

Do not cache Graphics objects. Graphics objects are defined to be stateless. As a consequence of their statelessness, if you cache a Graphics object for a form and the form is resized, the cached Graphics object will not render the output correctly. Each time you need a Graphics object to paint a particular control, call CreateGraphics. You can use a single Graphics instance within a single method and called methods; when the method that created the Graphics object exits, call Dispose.

Custom Graphics in the Paint Event

Another way you get a Graphics object is when you write an event handler for a control's Paint event. The first argument in the Paint event is the Object parameter and the second is a PaintEventArgs object. The parameter e contains a property that is an instance of the Graphics object for the control that raised the Paint event. Listing 17.1 demonstrates a shadowed text effect created in a form's Paint event.

Listing 17.1 Simple graphic effects, like shadowed text, require very little effort using GDI+
  1:  Private Sub ShadowText(ByVal G As Graphics, _  2:  ByVal Text As String, ByVal X As Integer, _  3:  ByVal Y As Integer)  4:   5:  G.DrawString(Text, _  6:  New Font("Times New Roman", 16, FontStyle.Bold), _  7:  Brushes.Silver, X - 3, Y - 3)  8:   9:  G.DrawString(Text, _  10:  New Font("Times New Roman", 16, FontStyle.Bold), _  11:  Brushes.Black, X, Y)  12:   13:  End Sub  14:   15:  Private Sub Form1_Paint(ByVal sender As Object, _  16:  ByVal e As System.Windows.Forms.PaintEventArgs) _  17:  Handles MyBase.Paint  18:   19:  ShadowText(e.Graphics, "GOTDOTNET", 11, 11)  20:  End Sub 

Line 19 calls a well-named method, passing basic information to create a shadowed-text effect. The Graphics.DrawString method is called on lines 5 and 9. The first time the text is drawn, the X and Y values are offset and the text is drawn using a Silver brush. The foreground text is drawn at the X and Y positions using a darker , contrasting foreground brush color .


You could create custom text in VB6. There are, however, some distinct differences between VB6 and VB .NET. In VB6 you probably had to import API methods, like WriteText and GetDC. Also, in VB6 many controls did not raise a Paint event.

With Visual Basic .NET, more controls implement the Paint method and raise a Paint event, enabling you to perform custom painting on a wider variety of Windows Forms controls.

Using variations of offsetting values, you can create embossed , shadowed, engraved, or outlined text with the code demonstrated in the listing.

Basic Drawing Classes

The System.Drawing namespace contains fundamental classes for drawing. GDI+ is a stateless implementation. The result is that you will need to pass in instances of fonts, colors, brushes, pens, and shapes depending on the needs of the class and methods each time you invoke a drawing operation.

There are collections, such as Brushes, that contain dozens of properties that return predefined items (such as brushes), making the task of acquiring a brush (color, pen, and so on) easier.

Color Class

The Color class implements about 50 Shared properties that return a specific color value. (The list is extensive enough to include colors like AliceBlue.) Use the class and refer to the color property by name to get an instance of that color. For example, Color.AliceBlue returns an instance of a Color object that is the color AliceBlue. (I wonder what Color.AliceCooper would return?)

The Color class also allows you to manually initialize a Color object and define the color by specifying the A, R, G, and B values. R, G, and B are the red, green, and blue percentages, and A is the alpha component of the color. (The alpha part of color has to do with the transparency of a color relative to the background.)

Additionally, you can create a Color by calling the shared methods FromArgb or FromName. For example, BackColor = Color.FromName("PapayaWhip") will turn the background color of a form to a salmony-beige color. If you are comfortable with or want to experiment with RGB colorsor know a color by nameyou can get Color objects using these two shared methods.

Brushes Class

The Brushes class is similar to the Color class in the members it contains. Brushes contains shared properties defined by the color of the brush and has properties with the same names as the color properties. Keep in mind that Brushes returns an instance of a Brush object, not a color.

It would have been consistent if there were simply a Brush class with the shared properties that returned brushes, but if you need an instance of a custom Brush, you have to construct an instance of the Brush class, not Brushes. (There is no Colors equivalent.)

A Brush is used to describe how the interiors of shapes are filled, including the color component. (See Listing 17.1 for an example of how to use the Brushes class in context.)

Pens Class

The Pens class is analogous to the Brushes class. There are shared properties named by the color of the pen that you can access to quickly get a Pen object. Pens are used to define the color of lines and curves. If you need to manually create an instance of a Pen, you will need to construct a Pen, rather than a Pens object. (See the section on the Rectangle class for an example of using the Pens class.)

Simple GDI+ Operation

Using the simple primitives, you can provide the appropriate arguments necessary to satisfy the stateless behavior of GDI+. Listing 17.2 demonstrates using the Pens, Graphics, and RectangleF classes to perform some basic architectural drawing.

Listing 17.2 Basic graphics example using Pens, Graphics, and RectangleF
  1:  Private Sub DrawArc(ByVal G As Graphics)  2:  Const Max As Integer = 36  3:  Dim I As Integer  4:  For I = 1 To Max  5:  G.DrawArc(Pens.Red, RandomRect(Max), 0, 180)  6:  Next  7:  End Sub  8:   9:  Private Function RandomRect(_  10:  Optional ByVal Max As Integer = 36) As RectangleF  11:   12:  Static Count As Integer = 0  13:  If (Count >= Max) Then Count = 0  14:  Count += 1  15:   16:  Return New RectangleF(_  17:  (ClientRectangle.Width - (Count * 10)) / 2, _  18:  10, Count * 10, 300)  19:   20:  End Function 

The DrawArc procedure beginning on line 1 is passed an instance of a Graphics object and draws 36 arcs using the Red pen property from the Pens class. The last two parameters of DrawArc express the start angle and sweep. The parameters 0 and 180 draw the bottom half of an ellipse. The RectangleF object returned by RandomRect defines a rectangle that acts as the constraining boundary of the ellipse. (See the upcoming sections on DrawArc and Rectangle for some additional information.)

Drawing Shapes and Text

Using the primitives for pens, colors, brushes, and fonts, GDI+ provides methods for drawing shapes, text, and graphics, like icons.

Rectangle and RectangleF Structures

There seems to be little comparative difference between the ValueType structures RectangleF and Rectangle. Some methods seem to require the RectangleF and others the Rectangle. Rectangles are structures that define a rectangular area by tracking the X and Y ordered pair and Width and Height properties.


The difference between structures like Rectangle and RectangleF is that the suffix indicates that the fields are floating-point rather than integer fields.

There are overloaded versions of methods that take both integer and floating-point versions of similar structures. Why this difference exists is presumed to be related to the range of possible values supported by each type.

Recall that you do not have to use the New keyword when declaring a ValueType variable; however, if you want to call a parameterized constructor for a ValueType, declare the variable with the New keyword. For example, the following line declares a variable R that is a ready-to-use Rectangle:

 Dim R As Rectangle 

If you want to initialize the Rectangle with the X, Y and Width, Height arguments, use the parameterized constructor version with the New keyword:

 Dim R As New Rectangle(1,1, 10,10) 

The Cartesian quadrant in Windows Forms is quadrant II, where the origin 0, 0 is the upper-left corner of the Form (or control).

If you need more advanced region management than provided by the Rectangle or RectangleF structures, use the Region class. Region is covered in the section titled, "Advanced Drawing." (For additional code samples demonstrating Region, refer to Chapter 15, "Using Windows Forms.")

Point and PointF Structures

Point and PointF are ValueTypes that represent an X,Y ordered-pair in the Cartesian plane. Points are used to represent a position in a two-dimensional drawing surface, a Windows Form.


The Graphics.DrawArc method is used to draw arcs (see Figure 17.1), or part or all of an ellipse. If you want a closed arc, call DrawEllipse. If you want an open arc, use DrawArc.

Figure 17.1. The Graphics. DrawArc method employs a Pen, a bounding Rectangle, a start angle, and the sweep of the arc.


There are four versions of DrawArc. Essentially an arc requires a Pen, a bounding Rectangle, a start angle, and the sweep of the arc. A start angle of 0 is the right-horizontal from the center of the arc, and the sweep proceeds clockwise. For example, a start angle of 0 and a sweep of 360 would draw a closed arc. Figure 17.1 illustrates the 0-angle and direction and relative position of a 90-degree sweep.


The Graphics.DrawEllipse method draws a closed arc, so it does not need the angle and sweep parameters. The Pen argument describes the color of the line used to draw the ellipse, and the Rectangle parameter defines the bounding region.

Calling Graphics.DrawEllipse(Pens.Blue, New RectangleF(10, 10, 100, 50) yields the same result as Graphics.Draw(Pens.Blue, New RectangleF(10, 10, 100, 50), 0, 360). Replace the call to DrawArc on line 5 of Listing 17.2, losing the angle and sweep arguments to create the visual effect of a 3D sphere.


Graphics.DrawRectangle has three overloaded versions that take a Pen and a Rectangle object or the four coordinates of a rectangle.


Polygons are many-sided shapes whose edges are defined by connecting points. Graphics.DrawPolygon takes a Pen argument and an array of points. The first point is the last point; consequently, you do not have to repeat the first point to close the polygon. The number of sides in the polygon will be equal to the number of points, although if you define only two points, the polygon will appear to be a line.

Listing 17.3 demonstrates an array of five points that roughly define a pentagon.

Listing 17.3 Using Graphics.DrawPolygon to draw a pentagon
  1:  Private Sub DrawPolygon(ByVal G As Graphics)  2:   3:  Dim Points() As Point = {New Point(10, 30), _  4:  New Point(100, 10), New Point(150, 75), _  5:  New Point(100, 150), New Point(10, 130)}  6:   7:  G.DrawPolygon(Pens.Purple, Points)  8:   9:  End Sub 

The Point type is a ValueType; however, to use the overloaded, parameterized constructor of a ValueType, we need to use the New keyword. Points defines an array of five point structures. DrawPolygon, on line 7, draws the polygon defined by the array of points. DrawPolygon closes the polygon defined by the points, mitigating the need for specifying a last point matching the first point.


The Graphics.DrawPie method works similarly to the DrawArc method. DrawPie takes a bounding rectangle, a start angle, and a sweep. The distinction between DrawArc and DrawPie is that the latter closes the arc with line segments. Listing 17.4 demonstrates the DrawPie method, using a TextureBrush, to fill a wedge of the pie (see Figure 17.2).

Figure 17.2. A TextureBrush filled pie wedge using methods of the Graphics object.


Listing 17.4 Calling the Graphics.DrawPie method and a Brush subclass, TextureBrush to fill a pie wedge
  1:  Private Sub DrawPie(ByVal G As Graphics)  2:  Dim R As Rectangle = New Rectangle(50, 50, 125, 150)  3:  Dim B As New TextureBrush(Icon.ToBitmap)  4:   5:  G.FillPie(B, R, 0, 90)  6:  G.DrawPie(Pens.Black, R, 0, 270)  7:  End Sub 

Line 2 calls the parameterized constructor for the Rectangle ValueType, so we must invoke the constructor using New. Line 3 creates a subclass of BrushTextureBrushusing the form's Icon to create the texture. (TextureBrush is overloaded and takes a Bitmap; to satisfy the overloaded TextureBrush constructor, we call Icon.ToBitmap to convert the icon to a bitmap.) Line 5 creates the wedge first using the texture brush and line 6 draws the pie, outlining the wedges.


As demonstrated in this chapter's examples so far, rendering graphics-based images is much easier in Visual Basic .NET. You can also use some of the techniques demonstrated thus far to create some neat string-drawing effects.

The basic Graphics.DrawString method takes a string, font, brush, and an ordered-pair indicating the starting left-top position of the text. The color of the string is determined by the font argument, and the fill characteristics are determined by the brush. For example, if you pass a textured brush, you can get some unique-looking strings. Listing 17.5 demonstrates using a textured brush that uses the JPEG image of this book's jacket to create some fiery text.

Listing 17.5 Employs a textured brush to create some unique-looking text
  1:  Private Sub DrawText(ByVal G As Graphics)  2:  Dim Image As New Bitmap("..\ ..\ Images\ VBNET.jpg")  3:   4:  Dim ATextureBrush As New TextureBrush(Image)  5:  Dim AFont As New Font("Garamond", 50, _  6:  FontStyle.Bold, GraphicsUnit.Pixel, 0)  7:   8:  G.DrawString("Unleashed!", AFont, ATextureBrush, 10, 10)  9:  End Sub 

Line 2 creates a Bitmap object from a JPEG file, which provides for high levels of compression. (You might want to double-check the location and existence of the image file in production code.) Line 4 instantiates a TextureBrush from the JPEG image. Line 5 instantiates a Garamond bold font 50 pixels high at the ordered-pair position X = 10 and Y = 10. Line 8 invokes the DrawString method passing a literal string message, the font and brush objects, and the X,Y offset.

You could use an existing Brush from the Brushes class and the form's Font, resulting in a simpler appearance that requires fewer lines of code: G.DrawString("Unleashed!", 50, Font, Brushes.Orange, 10, 10). The previous statement uses an existing font and brush and would output the text with a single statement.


Visual BasicR. NET Unleashed
Visual BasicR. NET Unleashed
Year: 2001
Pages: 222 © 2008-2017.
If you may any questions please contact us: