Drawing and Printing in C#
- Understanding the Graphics Object
- Working with Pens
- Using System Colors
- Working with Rectangles
- Drawing Shapes
- Drawing Text
- Persisting Graphics on a Form
- Build a Graphics Project Example
- Summary
- Q&A
- Workshop
- Answers
C# provides an amazingly powerful array of drawing capabilities. However, this power comes at the price of a steep learning curve. Drawing isn't intuitive; you can't sit down for a few minutes with the online Help text and start drawing graphics. After you learn the basic principles involved, however, you'll find that drawing isn't that complicated. In this hour, you'll learn the basic skills for drawing shapes and text to a form or other graphical surface. You'll learn about pens, colors, and brushes. In addition, you'll learn how to persist graphics on a formand even how to create bitmaps that exist solely in memory.
The highlights of this hour include the following:
- Understanding the Graphics object
- Working with pens
- Using system colors
- Working with rectangles
- Drawing shapes
- Drawing text
- Persisting graphics on a form
Understanding the Graphics Object
The code within the Windows operating system that handles drawing everything to the screen, including text, lines, and shapes, is called the Graphics Device Interface (GDI). The GDI processes all drawing instructions from applications as well as from Windows itself, and generates the proper output for the current display. Because the GDI generates what you see onscreen, it has the responsibility of dealing with the particular display driver installed on the computer and the settings of the driver, such as resolution and color depth. This means that applications don't have to worry about these details; you write code that tells the GDI what to output, and the GDI does whatever is necessary to produce that output. This behavior is called device independence because applications can instruct the GDI to display text and graphics using code that is independent of the particular display device.
C# code communicates with the GDI primarily via a Graphics object. The basic process is the following:
An object variable is created to hold a reference to a Graphics object.
The object variable is set to a valid Graphics object (new or existing).
To draw or print, you call methods of the Graphics object.
Creating a Graphics Object for a Form or Control
If you want to draw directly to a form or control, you can easily get a reference to the drawing surface by calling the CreateGraphics() method of the object in question. For example, to create a Graphics object that draws to a text box, you could use code such as this:
System.Drawing.Graphics objGraphics; objGraphics = this.textBox1.CreateGraphics();
When you call CreateGraphics(), you're setting the object variable to hold a reference to the Graphics object of the form or control's client area. The client area of a form is the gray area within the borders and title bar of the form. The client area of a control is usually the entire control. All drawing and printing done using the Graphics object is sent to the client area. In the code shown previously, the Graphics object references the client area of a text box, so all drawing methods called on the Graphics object would draw on the text box only.
NOTE
When you draw directly to a form or control, the object in question doesn't persist what is drawn on it. If the form is obscured in any way, such as by a window covering it or by minimizing the form, the next time the form is painted, it won't contain anything that was drawn on it. Later in this hour, I'll teach you how to persist graphics on a form.
Creating a Graphics Object for a New Bitmap
You don't have to set a Graphics object to the client area of a form or control; you can also set a Graphics object to a bitmap that exists only in memory. For performance reasons, you might want to use a memory bitmap to store temporary images or to use as a place to build complex graphics before sending them to a visible element. To do this, you first have to create a new bitmap. To create a new bitmap, you declare a variable to hold a reference to the new bitmap and then you create a new bitmap using the following syntax:
Bitmap variable = new Bitmap(width, height, pixelformat);
The width and height arguments are exactly what they appear to be: the width and height of the new bitmap. The pixelformat argument, however, is less intuitive. This argument determines the color depth of the bitmap and may also specify whether the bitmap has an alpha layer (used for transparent portions of bitmaps). Table 1 lists a few of the common values for PixelFormat (see C#'s online Help for the complete list of values and their meanings). Note that the pixelformat parameter is referenced as System.Drawing.Imaging.PixelFormat.formatenumeration.
Table 1Common Values for PixelFormat
Value | Description |
Format16bppGrayScale | The pixel format is 16 bits per pixel. The color information specifies 65,536 shades of gray. |
Format16bppRgb555 | The pixel format is 16 bits per pixel. The color information specifies 32,768 shades of color, of which five bits are red, five bits are green, and five bits are blue. |
Format24bppRgb | The pixel format is 24 bits per pixel. The color information specifies 16,777,216 shades of color, of which eight bits are red, eight bits are green, and eight bits are blue. |
For example, to create a new bitmap that is 640 pixels wide by 480 pixels tall and has a pixel depth of 24 bits, you could use the following statement:
objMyBitMap = new Bitmap(640, 480, System.Drawing.Imaging.PixelFormat.Format24bppRgb);
After the bitmap is created, you can create a Graphics object that references the bitmap using the FromImage() method, like this:
objGraphics = Graphics.FromImage(objMyBitMap);
Now, any drawing or printing done using objGraphics would be performed on the memory bitmap. For the user to see the bitmap, you'd have to send the bitmap to a form or control. You'll do this in the section "Persisting Graphics on a Form."
Disposing of an Object
When you're finished with a Graphics object, you should call its Dispose() method to ensure that all resources used by the Graphics object are freed. Simply letting an object variable go out of scope doesn't ensure that the resources used by the object are freed. Graphics objects can use considerable resources, so you should always call Dispose() when you're finished with any Graphics object (including Pens and other types of objects).
C# also supports a way of automatically disposing object resources. This can be accomplished utilizing C#'s using statement. The using statement wraps a declared object or objects in a block and disposes of those objects once the block is done. As a result, after the code is executed in a block, the block is exited, and the resources are disposed of upon exit. Following is the syntax for the using statement and a small sample:
using (expression | type identifier = initializer) { // Statements to execute } using (MyClass objClass = new MyClass()) { objClass.Method1(); objClass.Method2(); }
One thing to keep in mind is that the using statement acts as a wrapper for an object within a specified block of code; therefore, it is only useful for declaring objects that are used and scoped within a method.