The Graphics Class
The majority of the GDI+ drawing smarts is concentrated in the Graphics class. Table 12-1 describes the basic set of Graphics class members, many of which are explored in detail as the chapter progresses.
Table 12-1: Basic Graphics Class Members
MemberDescription
CompositingMode and CompositingQualityCompositingMode determines whether the drawing will overwrite the background or be blended with it. The CompositingQuality specifies the technique that will be used when blending, which determines the quality and speed of the operation.
InterpolationModeDetermines how properties are specified between the start point and end point of a shape (for example, when drawing a curve).
SmoothingMode and TextRenderingHintThese properties set the rendering quality (and optionally, the antialiasing) that will be used for drawing graphics or text on this GDI+ surface.
Clear()Clears the entire drawing surface and fills it with the specified background color.
Dispose()Releases all the resources held by the graphics object.
FromHdc(), FromHwnd(), and FromImage()These static methods create a Graphics object using either a handle to a device context, a window, or a .NET Image object.
GetHdc() and RemoveHdc()GetHdc() gets the Windows GDI handle that you can use with unmanaged code (for example, methods in the gdi32.dll library). You should use the RemoveHdc() method to release the device context when you are finished, before the Graphics object is disposed of.
IsVisible()Accepts a point object, and indicates whether this point is in a visible portion of the graphics device (not outside the clipping region). This does not depend on whether the window itself is actually visible on the screen.
MeasureString()Returns a Size structure that indicates the amount of space that is required for a given string of text in a given font. This is an extremely important method when handling wrapped printing or drawing a multiline text display.
Save() and Restore()Save() stores the state of the current Graphics object in a GraphicState object. You can use this object with the Restore() method. This is typically used when you are changing the GDI+ surface coordinate systems.
SetClip()Allows you to define the clipping region of this device context using a Rectangle, Region, or GraphicsPath. When you paint content on this surface, the only portions that appear are those that lie inside the clipping region.
The Graphics class also provides a slew of methods for drawing specific shapes, images, or text. Most of these methods begin with the word "Draw." All shape-drawing methods draw outlines; you need to use the corresponding "Fill" method to paint an interior fill region.Most of the methods in Table 12-2 are self-explanatory. Two interesting methods that I haven't described yet include DrawPath() and FillPath(), which work with the GraphicsPath class in the System.Drawing.Drawing2D namespace.
Table 12-2: Graphics Class Methods for Drawing
MethodDescription
DrawArc()Draws an arc representing a portion of an ellipse specified by a pair of coordinates, a width, and a height.
DrawBezier() and DrawBeziers()The infamous and attractive Bezier curve, which is defined by four control points.
DrawClosedCurve()Draws a curve, and then closes if off by connecting the end points.
DrawCurve()Draws a curve (technically, a cardinal spline).
DrawEllipse()Draws an ellipse defined by a bounding rectangle specified by a pair of coordinates, a height, and a width.
DrawIcon() and DrawIconUnstreched()Draws the icon represented by an Icon object, and (optionally) stretches it to fit a given rectangle.
DrawImage and DrawImageUnscaled()Draws the image represented by an Image-derived object, and (optionally) stretches it to fit a given rectangle.
DrawLine() and DrawLines()Draws a line connecting the two points specified by coordinate pairs.
DrawPath()Draws a GraphicsPath object, which can represent a combination of curves and shapes.
DrawPie()Draws a "piece of pie" shape defined by an ellipse specified by a coordinate pair, a width, a height, and two radial lines.
DrawPolygon()Draws a multisided polygon defined by an array of points.
DrawRectangle() and DrawRectangles()Draws an ordinary rectangle specified by a starting coordinate pair and width and height.
DrawString()Draws a string of text in a given font.
FillClosedCurve()Draws a curve, closes if off by connecting the end points, and fills it.
FillEllipse()Fills the interior of an ellipse.
FillPath()Fills the shape represented by a GraphicsPath object.
FillPie()Fills the interior of a "piece of pie" shape.
FillPolygon()Fills the interior of a polygon.
FillRectange() and FillRectanges()Fills the interior of a rectangle.
FillRegion()Fills the interior of a Region object.
The GraphicsPath class encapsulates a series of connected lines, curves, and text. You used the GraphicsPath class in Table 12-3 to add all the required elements.
GraphicsPath path = new GraphicsPath();
path.AddEllipse(0, 0, 100, 50);
path.AddRectangle(New Rectangle(100, 50, 100, 50);
Table 12-3: GraphicsPath Methods
MethodDescription
AddArc()Draws an arc representing a portion of an ellipse specified by a pair of coordinates, a width, and a height.
AddBezier() and AddBeziers()The infamous and attractive Bezier curve, which is defined by four control points.
AddClosedCurve()Draws a curve, and then closes if off by connecting the end points.
AddCurve()Draws a curve (technically, a cardinal spline).
AddEllipse()Draws an ellipse defined by a bounding rectangle specified by a pair of coordinates, a height, and a width.
AddLine() and AddLines()Draws a line connecting the two points specified by coordinate pairs.
AddPath()Adds another GraphicsPath object to this GraphicsPath object.
AddPie()Draws a "piece of pie" shape defined by an ellipse specified by a coordinate pair, a width, a height, and two radial lines.
AddPolygon()Draws a multisided polygon defined by an array of points.
AddRectangle() and AddRectangles()Draws an ordinary rectangle specified by a starting coordinate pair and width and height.
AddString()Draws a string of text in a given font.
StartFigure() and CloseFigure()StartFigure() defines the start of a new closed figure. When you use CloseFigure(), the starting point will be joined to the end point by an additional line.
Transform(), Warp(), and Widen()Used to apply a matrix transform, a warp transform (defined by a rectangle and parallelogram), or an expansion, respectively.
Optionally, you can also create a solid filled figure out of lines. To do this, you first call the StartFigure() method. Then you add the required curves and lines using the appropriate methods. When finished, you call the CloseFigure() method to close off the shape by drawing a line from the endpoint to the starting point. You can use these methods multiple times to add several closed figures to a single GraphicsPath object.
GraphicsPath path = new GraphicsPath();
path.StartFigure();
path.AddArc(10, 10, 100, 100, 20, 50);
path.AddLine(20, 100, 70, 230);
path.CloseFigure();
Optionally, you can create a solid-filled figure out of lines. To do this, you first call the StartFigure() method. Then you add the required curves and lines using the appropriate methods. When finished, you call the CloseFigure() method to close off the shape by drawing a line from the endpoint to the starting point. You can use these methods multiple times to add several closed figures to a single GraphicsPath object.
GraphicsPath path = new GraphicsPath();
path.StartFigure();
path.AddArc(10, 10, 100, 100, 20, 50);
path.AddLine(20, 100, 70, 230);
path.CloseFigure();
Coordinate Systems and Transformations
By default, when you draw GDI+ shapes, you use a coordinate system that designates the top left corner as (0, 0). The x-axis value increases as you move to the right, and the y-axis value increases as you move down. The point (this.Width, this.Height) corresponds to the bottom-right corner of a form (discounting the title bar region). Each unit corresponds to one pixel. This is nothing new-it's the same coordinate system you examined when I introduced control basics in Chapter 3. However, the Graphics class also gives you the flexibility to change the unit of measurement, point of origin, and rotation.To change the unit of measurement, you simply set the PageUnit property of the Graphics class. You can use one of several values from the GraphicsUnitClass, including Display (1/75 of an inch), Document (1/300 inch), Inch, Millimeter, Pixel (the default), and Point (1/72 of an inch).
e.Graphics.PageUnit = Graphics.Inch;
The ability to change the point of origin is more useful. It uses the Graphics.TranslateTranform() method, which accepts the coordinates of the new point that should become (0,0). Using the code below, the point at (50, 50) will become the new (0,0) origin. Points to the left or right of this origin must be specified using negative values.
e.Graphics.TranslateTransform(50, 50);
This is a fairly handy trick. For example, it can allow you to perform simple calculations by assuming the top left point of your drawing is (0, 0), but gives you the freedom to add a border between the drawing and the form by translating the coordinate system before you begin to draw. You could even use this method several times with different points and repeat the same drawing code. The figure you are drawing would then appear at several different points in the window, as shown in Figure 12-10.
private void Transform_Paint(object sender,
System.Windows.Forms.PaintEventArgs e)
{
// Draw several squares in different places.
DrawRectangle(e.Graphics);
e.Graphics.TranslateTransform(180, 60);
DrawRectangle(e.Graphics);
e.Graphics.TranslateTransform(-50, 80);
DrawRectangle(e.Graphics);
e.Graphics.TranslateTransform(-100, 50);
DrawRectangle(e.Graphics);
}
private void DrawRectangle(Graphics g)
{
Pen drawingPen = new Pen(Color.Red, 30);
// Draw a rectangle at a fixed position.
g.DrawRectangle(drawingPen, new Rectangle(20, 20, 20, 20));
}

Figure 12-10: Using translate transforms
Note
Tranforms are cumulative, so transforming by (50, 50) and then (20,10) is equivalent to a single (70, 60) transform.The final transformation considered here is a rotational one. It uses the Graphics.RotateTransform() method, which rotates the coordinate system using an angle or matrix. The important fact to remember is that rotations are performed around the point of origin. If you haven't performed any translation transformations, this will be in the top right corner of the form.The next example uses a translation transform to move the center point to the middle of the form, and then rotates text around that point with successive rotational transforms. The result is shown in Figure 12-11.
private void RotateTransform_Paint(object sender,
System.Windows.Forms.PaintEventArgs e)
{
// Optimize text quality.
e.Graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
// Move origin to center of form so we can rotate around that.
e.Graphics.TranslateTransform(this.Width / 2 - 30, this.Height / 2 - 30);
DrawText(e.Graphics);
e.Graphics.RotateTransform(45);
DrawText(e.Graphics);
e.Graphics.RotateTransform(75);
DrawText(e.Graphics);
e.Graphics.RotateTransform(160);
DrawText(e.Graphics);
}
private void DrawText(Graphics g)
{
g.DrawString("Text", new Font("Verdana", 30, FontStyle.Bold),
Brushes.Black, 0, 10);
}

Figure 12-11: Using rotational transforms
Pens
In Chapter 3, you learned about many of the GDI+ basics, including fonts, colors, points, and rectangles. However, GDI+ drawing code also uses other details like brushes and pens.Pens are used to draw lines when you use the shape or curve drawing methods from the Graphics class. You can retrieve a standard pen using one of the static properties from the System.Drawing.Pens class. These pens all have a width of 1; they only differ in their color.
Pen myPen = Pens.Black;
You can also create a Pen object on your own, and configure all the properties described in Table 12-4.
Pen myPen = new Pen(Color.Red);
myPen.DashCap = DashCap.Triangle;
myPen.DashStyle = DashDotDot;
e.Graphics.DrawLine(myPen, 0, 0, 10, 0);
Table 12-4: Pen Members
MemberDescription
DashPatternDefines a dash style for broken lines using an array of dashes and spaces.
DashStyleDefines a dash style for broken lines using the DashStyle enumeration.
LineJoinDefines how overlapping lines in a shape will be joined together.
PenTypeThe type of fill that will be used for the line. Typically this will be SolidColor, but you can also use a gradient, bitmap texture, or hatch pattern by supplying a brush object when you create the pen. You cannot set the PenType through this property, however, as it is read-only.
StartCap and EndCapDetermines how the beginning and ends of lines will be rendered. You can also define a custom line cap by creating a CustomLineCap object (typically by using a GraphicsPath), and then assigning it to the CustomStartCap or CustomEndCap property.
WidthThe pixel width of lines drawn by this pen.
Figure 12-12 shows different line caps (which determine the appearance of the start and end of a line), while Figure 12-13 shows different dash styles.

Figure 12-12: Line caps

Figure 12-13: Dash styles
Tip
GDI+ differs from the traditional world of GDI programming in that it isn't stateful. That means that you need to keep track of pens on your own and submit the appropriate pen with every call to a draw method.
Brushes
Brushes are used to fill the space between lines. Brushes are used when drawing text or when using any of the fill methods of the Graphics class for painting the inside of a shape.You can quickly retrieve a predefined solid brush using a static property from the Brushes class, or the SystemBrushes class (which provides brushes that correspond to various Windows color scheme settings, like the control background color or the highlight menu text color).
Brush myBrush = SystemBrushes.Menu;
e.Graphics.FillRectangle(myBrush, 0, 0, 50, 50);
Last, you can create a custom brush. You need to decide what type of brush you are creating. Solid brushes are created from the SolidBrush class, while other classes (HatchBrush, LinearGradientBrush, and TextureBrush) allow fancier options. The next three sections consider these different types of brushes.
Tip
You can also create a pen that draws using the fill style of a brush. This allows you to draw lines that are filled with gradients and textures. To do so, begin by creating the appropriate brush, and then create a new pen. One of the overloaded pen constructor methods accepts a reference to a brush-that's the one you need to use for a brush-based pen.The HatchBrush
A HatchBrush has a foreground color, a background color, and a hatch style that determines how these colors are combined. Typically, colors are interspersed using stripes, grids, or dots, but you can even select unusual pattern styles like bricks, confetti, weave, and shingles.Following is the code for a simple brush demonstration program that displays the available hatch brush styles. Figure 12-14 shows the result.
private void HatchBrushes_Paint(object sender,
System.Windows.Forms.PaintEventArgs e)
{
HatchBrush myBrush;
int y = 20;
int x = 20;
// Enumerate over all the styles.
foreach (HatchStyle brushStyle in System.Enum.GetValues(typeof(HatchStyle)))
{
myBrush = new HatchBrush(brushStyle, Color.Blue, Color.LightYellow);
// Fill a rectangle with the brush.
e.Graphics.FillRectangle(myBrush, x, y, 40, 20);
// Display the brush name.
e.Graphics.DrawString(brushStyle.ToString(), new Font("Tahoma", 8),
Brushes.Black, 50 + x, y + 5);
y += 30;
if ((y + 30) > this.ClientSize.Height)
{
y = 20;
x += 180;
}
}
}

Figure 12-14: HatchBrush styles
The LinearGradientBrush
The LinearGradientBrush allows you to blend two colors in a gradient pattern. You can choose any two colors (as with the hatch brush) and then choose to blend horizontally (from left to right), vertically (from top to bottom), diagonally (from the top-left corner to the bottom-right), or diagonally backward (from the top-right to the bottom-left). You can also specify the origin point for either side of the gradient.Figure 12-15 shows the different gradient styles.

Figure 12-15: The LinearGradient brush
The TextureBrush
Finally, the TextureBrush attaches a bitmap to a brush. The image is tiled in the painted portion of the brush, whether it is text or a simple rectangle. Here's an example that fills a form with a tiled bitmap. The result is shown in Figure 12-16.
private void TextureBrushExample_Paint(object sender,
System.Windows.Forms.PaintEventArgs e)
{
TextureBrush myBrush = new TextureBrush(Image.FromFile("tile.bmp"));
e.Graphics.FillRectangle(myBrush, e.Graphics.ClipBounds);
}

Figure 12-16: The TextureBrush