Drawing in the .NET Framework


There are two types of drawing applications: raster based and vector based. Raster-based drawing applications work on bitmap images. Examples of these include the Microsoft Paint application that you can find in every computer with the Windows operating system. Shapes you draw in a raster-based drawing application blend into the drawing surface. Once drawn, the shape becomes part of the image. You cannot manipulate it as an object. Vector-based drawing applications, on the other hand, treat each shape drawn as an object. This means, once drawn, a shape can still be manipulated. You can resize it, change its color, fill it with a different color, and so on. Corel Draw is a popular example of a vector- based drawing application.

As mentioned previously, the main technology for working with graphics and text in the .NET Framework is GDI+. The beauty of GDI+ is that it is device independent. You use the same function to draw a particular shape without having to be concerned about the details of the device hardware to which you are drawing. All you need to do is call the methods in the classes that encapsulate GDI+ functionality.

GDI+ provides three categories of services:

  • Two-dimensional (2D) vector graphics

  • Imaging

  • Typography

The 2D vector graphics service deals with drawing shapes using points on a coordinate system. The shapes can be as primitive as a line (specified by two points on a coordinate system) and a rectangle (defined by four points). However, they can also be as complex as polygon splines and B zier splines.

You use the imaging service to manipulate bitmap images, and you use the typography service to manipulate text.

Note

The project in this chapter only uses the 2D vector graphics service.

The three GDI+ services are encapsulated in various classes in the .NET Framework class library. You can use a number of namespaces for working with graphics and text. The main one is the System.Drawing namespace. The other namespaces are System.Drawing.Design, System.Drawing.Drawing2D, System.Drawing.Imaging, System.Drawing.Printing, and System.Drawing.Text. The project uses a few classes and structures in the System.Drawing and the System.Drawing.Drawing2D namespaces. Therefore, you will look at these classes and structures in the following subsections.

The System.Drawing.Point Structure

A Point object represents a coordinate in a 2D plane. The easiest way to construct a Point is by passing two integers as the abscissa and the ordinate parts (the x and y parts) of the coordinate, such as the following:

 Dim point As Point = New Point(14, 200) 

Point is frequently used not only when drawing a shape but also when writing a form-based application. For example, to adjust the position of a Button control on a form, you can assign a Point object to the button's Location property to indicate the position of the top-left corner of the button on the form. As an example, the following code places the button's top-left corner at coordinate (100, 30) in a form that contains the button—in other words, 100 pixels from the left edge of the form and 30 pixels from the upper edge of the form's client area:

 button.Location = New Point(100, 30) 

The Point structure has the X and Y properties from which you can obtain the abscissa and ordinate of the coordinate that a Point object represents. Also, IsEmpty is a read-only property that returns True only when both the X and Y properties of a Point object have the value of zero. (Note, though, that this can be misleading; (0, 0) can represent a valid coordinate for a Point object.)

Another way of constructing a Point is by passing an Integer. The low-order 16 bits of the integer will become the value of the Point object's abscissa, and the high-order 16 bits the value of the Point object's ordinate.

If you need more precision in your Point object, you can use the PointF structure. Instead of representing a coordinate with a pair of integers, PointF takes Singles as the x and y parts of a coordinate. PointF has only a single constructor with the following signature:

 Public Sub New ( ByVal x as Single, ByVal y as Single ) 

The System.Drawing.Size Structure

The Size structure represents the width and height of a rectangular area. The easiest way to construct a Size object is by passing two integers as its width and height, like in the following code:

 Dim size As Size = New Size(10, 30) 

This Size object has a width of 10 pixels and a height of 30 pixels. In a form- based Windows application, the Size structure often sets or changes the size of a control. The Control class has the Size property to which you can assign a Size object. For instance, the following code sets the size of a button so that it will be 80-pixels wide and 30-pixels tall:

 button1.Size = New Size(80, 30) 

In addition, you can also construct a Size object by passing a Point object, like in the following code:

 Dim point As Point = New Point(50, 30) Dim size As Size = New Size(point) 

In this case, the width and height of the resulting Size object will have the value of X and Y properties of the Point object, respectively.

You can obtain the width and height values of a Size object by retrieving the value of its Width and Height properties. Another property, IsEmpty, is a read-only property that returns True only if both the Width and Height properties have the value of zero.

If you need more precision, you can use the System.Drawing.SizeF structure. SizeF is similar to Size except that Singles represents its width and height.

The System.Drawing.Rectangle Structure

The Rectangle structure represents a rectangle. You can use an instance of this structure for multiple purposes. For example, to draw a rectangle, you can pass a Rectangle object to the DrawRectangle method of the Graphics object (discussed in "The System.Drawing.Graphics Class" section).

An instance of the Rectangle structure stores the location of its top-left coordinate and its size. You can construct an instance of this structure by passing a Point and a Size of four integers, using the following two constructors of the Rectangle structure:

 Public Sub New( ByVal location As Point, ByVal size As Size) Public Sub New( _   ByVal x As Integer, _   ByVal y As Integer, _   ByVal width As Integer, _   ByVal height As Integer _ ) 

In this example, location in the first constructor, and x and y in the second constructor denote the top-left coordinate of the Rectangle.

The System.Drawing.Color Structure

The Color structure represents a color that you can use to draw shapes or to assign to the BackColor or ForeColor properties of a control. To construct a Color object, you do not use any constructor because this structure does not have one. Instead, the structure includes a large number of static (shared) properties that represent various colors. For example, the Brown property represents a brown Color object.

The following code shows how to use the Brown property of the Color structure to assign a brown color to the BackColor property of a Button control:

 Dim myColor As Color = Color.Brown button1.BackColor = myColor 

In addition to some common colors such as green, blue, yellow, red, white, and black, you can choose more exotic colors such as azure, beige, coral, or powder blue. There are more than 140 properties representing different colors!

If that is not enough, you can compose a custom color by passing the R, G, and B components of an RGB color to the FromArgb static method. Again, because this method is static, you can use it without having a Color object. As an example, the following code constructs a Color object whose R, G, and B components are all 240:

 Dim myColor As Color = Color.FromArgb(240, 240, 240) 

Note that even though the arguments are all integers, the values passed must be in the range of 0 and 255; this will result in a 32-bit color, in which the first eight bits are not used.

In fact, you can also specify all the 32 bits of a Color object through another overload of the FromArgb method that accepts four integers:

 Overloads Public Shared Function FromArgb( _ ByVal alpha As Integer, _ ByVal red As Integer, _ ByVal green As Integer, _ ByVal blue As Integer _ ) 

You can retrieve the individual alpha, red, green, and blue components of a Color object by calling its ToArgb method, which returns an Integer representing all the four components (alpha, red, green, and blue). This Integer indicates the Color object's color values as shown in Table 4-1.

Table 4-1: The Color Object's Color Values

BITS

VALUE

0–7

Blue

8–15

Green

16–23

Red

24–31

Alpha

Another way of constructing a Color object is by using the Color structure's FromName method. For example, the following code constructs a blue color by passing the string "blue" to the FromName method:

 Dim myColor As Color = Color.FromName("blue") 

The System.Drawing.Pen Class

The Pen class represents a pen that you can use for drawing a shape or writing text. A Pen object can have a color, a width, a brush, and other properties. The easiest way to construct a Pen is by passing a Color object to one of the Pen class's constructors. For instance, the following code creates a yellow pen:

 Dim pen As Pen = New Pen(Color.Yellow) 

A Pen object constructed using this code has a default width of 1 and a default brush. Another constructor lets you specify a width as well as a color. For example, the following Pen object is yellow with a width of 1.8:

 Dim pen As Pen = New Pen(Color.Yellow, 1.8) 

In addition, you can pass a Brush object, or a Brush object and a width, using two other constructors of the Pen class:

 Public Sub New( ByVal brush as Brush ) Public Sub New( ByVal brush as Brush, ByVal width As Single ) 

Note

For information on how to construct a Brush object, see "The System.Drawing.Brush Class" section.

The Pen class also provides some advanced properties to customize your Pen object. For example, you can create a Pen that draws a dashed line by specifying a value for the DashStyle property, as shown in the following code:

 pen.DashStyle = Drawing.Drawing2D.DashStyle.DashDot 

The System.Drawing namespace also provides an easy way to construct a Pen object with a width of 1: by using the Pens class. The Pens class has more than 100 properties whose names indicate a predefined color. From these properties you can get a Pen object in the specified color. For instance, the Green property of the Pens class returns a green Pen object with a width of 1. The Pens class's Red property gives you a red pen.

The System.Drawing.Brush Class

You use a Pen object to draw the lines of a shape or to write text on it. To fill the interior of a shape you draw, you use a brush, which in the .NET Framework class library is represented by the System.Drawing.Brush class and its five derived classes. The Brush class itself is an abstract class; therefore, you cannot directly instantiate it. You can, however, instantiate one of its child classes: HatchBrush, LinearGradientBrush, PathGradientBrush, SolidBrush, and TextureBrush. HatchBrush represents a hatch-styled rectangular brush, LinearGradientBrush represents a brush that paints with a linear gradient, PathGradientBrush fills a GraphicPath with a gradient, SolidBrush represents a brush that fills an area evenly, and TextureBrush represents a brush with a texture image to fill an area. For example, the following code draws a filled rectangle on a form using a SolidBrush:

 ' get the Graphics of a form Dim g As Graphics = form.CreateGraphics() Dim solidBrush As SolidBrush = New SolidBrush(Color.Blue) g.FillRectangle(solidBrush, 10, 20, 100, 100) 

The System.Drawing.Brushes class provides an easy way of obtaining a Brush object the same way the Pens class provides Pen objects in a specified color. For example, the Red property of the Brushes class returns a red Brush object. The other properties of a Brush object obtained from one of the properties of the Brushes class are system-defined.

The System.Drawing.Graphics Class

The System.Drawing.Graphics class represents a rectangular area on which various shapes can be drawn and text can be written. The constructor of this class is private, so there is no way you can instantiate a Graphics class using the New keyword. However, you can obtain a Graphics object from a form or another control. These Graphics objects represent the drawing surface of that control. Once you obtain a Graphics object, you can use its various methods to draw many different shapes on it. For example, if you want to draw on a form, you first need to obtain the Graphics object of that form and then call the methods of the Graphics object. The System.Windows.Forms.Control class provides the CreateGraphics method, which returns a Graphics object representing the drawing surface of that control.

The methods of the Graphics class often require you to pass a Pen or a Brush object to create a shape. As one example, you can use the DrawLine method of the Graphics class to draw a line. One of its overloads has the following signature:

 Overloads Public Sub DrawLine( _ ByVal pen As Pen, _ ByVal x1 As Integer, _ ByVal y1 As Integer, _ ByVal x2 As Integer, _ ByVal y2 As Integer _ ) 

This overload accepts a Pen object and four integers representing the starting coordinate (x1, y1) and the end coordinate (x2, y2) of the line. For example, the following code obtains the Graphics object of a form, creates a Pen object, and passes the Pen object to the DrawLine method to draw a line on the form:

 Dim g As Graphics = Me.CreateGraphics() Dim pen As Pen = New Pen(Color.Yellow, 1.9) ' set the pen's DashStyle to dash - dot pen.DashStyle = Drawing.Drawing2D.DashStyle.DashDot g.DrawLine(pen, 0, 0, 100, 200) 

The code draws the line from the coordinate (0, 0) to the coordinate (100, 200). Figure 4-1 shows a number of shapes you can draw using the methods of the Graphics class.

click to expand
Figure 4-1: Drawing simple shapes

The Graphics Class's Methods

The following are some other methods of the Graphics class.

DrawLines

The DrawLines method draws lines that connect a series of points in an array, which is passed as the second parameter to the method. The first parameter is a Pen object. For example, the following code declares and instantiates an array of four points and draws lines connecting those points on a form:

 Dim points(4) As Point points(0) = New Point(0, 0) points(1) = New Point(0, 120) points(2) = New Point(20, 120) points(3) = New Point(20, 0) Dim g As Graphics = Me.CreateGraphics() Dim pen As Pen = New Pen(Color.Yellow, 1.9) g.DrawLines(pen, points) 

DrawRectangle

The DrawRectangle method draws a rectangle. For example, the following code draws a rectangle on a form by passing a Pen object and four integers. The first two integers represent the coordinate of the top-left corner of the rectangle, the third coordinate is the width of the rectangle, and the last integer the height of the rectangle:

 Dim g As Graphics = Me.CreateGraphics Dim pen As Pen = New Pen(Color.Aquamarine, 1.9) g.DrawRectangle(pen, 10, 10, 100, 20) 

The top-left corner of the rectangle is at coordinate (10, 10), and it has a width of 100 pixels and a height of 20 pixels.

DrawEllipse

The DrawEllipse method draws an ellipse within an imaginary rectangle. For example, the following code draws an ellipse having a width of 100 pixels and a height of 20. The top-left corner of the imaginary rectangle is at coordinate (10, 10):

 Dim g As Graphics = Me.CreateGraphics() Dim pen As Pen = New Pen(Color.Bisque, 1.9) g.DrawEllipse(pen, 10, 10, 100, 20) 

FillRectangle

The FillRectangle method draws a filled rectangle. For example, the following code draws a filled rectangle on a form by passing a SolidBrush object and four integers. The first two integers represent the coordinate of the top-left corner of the rectangle. The rectangle has a width of 100 pixels and a height of 30 pixels:

 Dim g As Graphics = Me.CreateGraphics() Dim solidBrush As SolidBrush = New SolidBrush(Color.DarkCyan) g.FillRectangle(solidBrush, 10, 10, 100, 30) 

FillEllipse

The FillEllipse method draws a filled ellipse. This method is similar to DrawEllipse, but the resulting ellipse is filled. Instead of passing a Pen object, the method requires a Brush object. The following code draws a filled ellipse on a form:

 Dim g As Graphics = Me.CreateGraphics() Dim solidBrush As SolidBrush = New SolidBrush(Color.DeepPink) g.FillEllipse(solidBrush, 10, 10, 100, 30) 

DrawString

The DrawString method writes a string of text at a specified location on the Graphics object. For an example, see "The System.Drawing.Font Class" section later in this chapter.

Save

The Save method persists the current Graphics object into a System.Drawing.Drawing2D.GraphicsState object. Later, you can retrieve the persisted object using the Restore method.

Restore

The Restore method restores a previously persisted Graphics object from a System.Drawing.Drawing2D.GraphicState object. For example, the following code saves the state of a Graphics object into a System.Drawing.Drawing2D.GraphicState object, does something, and restores it:

 Dim g As Graphics = Me.CreateGraphics() ' save the state of the current Graphics object Dim graphicsState As GraphicsState = g.Save ' do something here   .   .   . ' restore it g.Restore(graphicsState) 

The Graphics Class's Properties

The following are some of the most important properties of the Graphics class.

DpiX

DpiX is the horizontal resolution of the current Graphics object.

DpiY

DpiX is the vertical resolution of the current Graphics object.

PageUnit

By default, the measurement unit of a Graphics object is a pixel. You can change this by setting the PageUnit property. This property takes one of the members of the GraphicsUnit enumeration: Display, Document, Inch, Millimeter, Pixel, Point, and World. For example, the following changes the PageUnit property of a Graphics object to inches:

 graphics.PageUnit = GraphicsUnit.Inch 

The System.Drawing.Font Class

The System.Drawing.Font class represents a font. The easiest way to construct a Font object is by passing a String containing the font name and the font size. For instance, the following obtains the Graphics object of a form, creates an Arial font with the size of 14, creates a SolidBrush called solidBrush, and writes a String on the Graphics object in 14-point Arial:

 Dim g As Graphics = Me.CreateGraphics() Dim font As Font = New Font("Arial", 14) Dim solidBrush As SolidBrush = New SolidBrush(Color.Chartreuse) g.DrawString("Hello", font, solidBrush, 10, 100) 

If the font name passed to the constructor does not correspond to an available font, the default font is used.

The Font class has read-only Bold and Italic properties that indicate whether the Font is bold or italic.

The System.Drawing.Drawing2D.GraphicsPath Class

The System.Drawing.Graphics class provides methods to draw various shapes. However, you can create your own custom shape if none of the methods meets your needs. To do this, you use the System.Drawing.Drawing2D.GraphicsPath class. The GraphicsPath class, as the name implies, represents a graphics path. A graphics path consists of a series of connected points. After you construct a GraphicsPath object, you can draw it using the Graphics class's DrawPath method.

Besides for creating a custom shape, you can also use the GraphicsPath to create a custom cap for your Pen object, as discussed in the "Pen Caps" section.

As an example, the following code creates a GraphicsPath object and draws it using the DrawPath method of the Graphics class:

 Dim gp As New GraphicsPath() gp.AddLine(New Point(80, 20), New Point(10, 30)) gp.AddArc(New Rectangle(10, 30, 80, 160), 40, 130) gp.AddLine(New Point(150, 30), New Point(80, 20)) graphics.DrawPath(Pens.Blue, gp) 

Figure 4-2 shows the result of this code.


Figure 4-2: A GraphicsPath object

Pen Caps

You have seen that you can construct a Pen object and use it to draw lines and other shapes. What you have not learned is that the Pen class has the StartCap and EndCap properties, which represent the cap styles when the Pen object draws a line. The StartCap property determines the cap style at the beginning of the line, and the EndCap property the cap style at the end of the line.

The StartCap and the EndCap properties can accept a member of the System.Drawing.Drawing2D.LineCap enumeration: AnchorMask, ArrowAnchor, Custom, DiamondAnchor, Flat, NoAnchor, Round, RoundAnchor, Square, SquareAnchor, and Triangle.

As an example, Listing 4-1 creates Font and Brush objects and uses different end caps with the Pen object named pen.

Listing 4-1: Using Different Caps

start example
 Dim font As New Font("Courier New", 10) Dim brush As New SolidBrush(Color.Black) pen.EndCap = LineCap.AnchorMask g.DrawLine(pen, 5, 10, 40, 10) g.DrawString("AnchorMask", font, brush, 75, 5) pen.EndCap = LineCap.ArrowAnchor g.DrawLine(pen, 5, 30, 40, 30) g.DrawString("ArrowAnchor", font, brush, 75, 25) pen.EndCap = LineCap.DiamondAnchor g.DrawLine(pen, 5, 50, 40, 50) g.DrawString("DiamondAnchor", font, brush, 75, 45) pen.EndCap = LineCap.Flat g.DrawLine(pen, 5, 70, 40, 70) g.DrawString("Flat", font, brush, 75, 65) pen.EndCap = LineCap.NoAnchor g.DrawLine(pen, 5, 90, 40, 90) g.DrawString("NoAnchor", font, brush, 75, 85) pen.EndCap = LineCap.Round g.DrawLine(pen, 5, 110, 40, 110) g.DrawString("Round", font, brush, 75, 105) pen.EndCap = LineCap.RoundAnchor g.DrawLine(pen, 5, 130, 40, 130) g.DrawString("RoundAnchor", font, brush, 75, 125) pen.EndCap = LineCap.Square g.DrawLine(pen, 5, 150, 40, 150) g.DrawString("Square", font, brush, 75, 145) pen.EndCap = LineCap.SquareAnchor g.DrawLine(pen, 5, 170, 40, 170) g.DrawString("SquareAnchor", font, brush, 75, 165) pen.EndCap = LineCap.Triangle g.DrawLine(pen, 5, 190, 40, 190) g.DrawString("Triangle", font, brush, 75, 185) 
end example

Figure 4-3 shows the result.

click to expand
Figure 4-3: Using different caps

Note that the nondefault caps do not affect other shapes than lines, as shown by the rectangle at the bottom of Figure 4-3.

However, the available caps are only apparent when the pen width is large enough. If you choose a width of 1, for example, the caps are not that obvious, as demonstrated in Figure 4-4.


Figure 4-4: Line caps are not obvious if the pen width is not large enough.

You can create your own cap by creating a System.Drawing.Drawing2D.CustomLineCap object and assigning it to the CustomStartCap and/or the CustomEndCap properties of a Pen object. You create a CustomLineCap by passing a GraphicsPath object. As an example, Listing 4-2 constructs a CustomLineCap object and assigns it to the CustomEndCap of a Pen object.

Listing 4-2: Drawing Your Own Cap

start example
 Dim g As Graphics = Me.CreateGraphics() Dim pen As New Pen(Color.Black, 1) Dim gp As New GraphicsPath() gp.AddLine(New Point(-20, 0), New Point(20, 0)) gp.AddLine(New Point(20, 0), New Point(0, 20)) gp.AddLine(New Point(0, 20), New Point(-20, 0)) Dim customCap As New CustomLineCap(Nothing, gp) pen.CustomEndCap = customCap g.DrawLine(pen, 50, 100, 50, 35) g.DrawLine(pen, 50, 150, 150, 100) g.DrawLine(pen, 50, 170, 150, 170) g.DrawLine(pen, 50, 180, 50, 250) 
end example

Figure 4-5 shows the result.

click to expand
Figure 4-5: Creating your own cap

Anti-Aliasing

When you draw a line or another shape, sometimes the shape appears jagged. GDI+ provides a way to smooth this out by applying anti-aliasing. Precisely, the Graphics class has a property called SmoothingMode. This property value is one of the System.Drawing.Drawing2D.SmoothingMode enumeration members: AntiAlias, Default, HighQuality, HighSpeed, Invalid, and None. You assign SmoothingMode.AntiAlias to apply anti-aliasing.

Listing 4-3 draws a number of lines. The first four lines drawn have no anti- aliasing applied to them. The lines afterward do.

Listing 4-3: Applying Anti-Aliasing

start example
 Dim g As Graphics = Me.CreateGraphics() Dim pen As New Pen(Color.Black, 1) Dim gp As New GraphicsPath() gp.AddLine(New Point(-20, 0), New Point(20, 0)) gp.AddLine(New Point(20, 0), New Point(0, 20)) gp.AddLine(New Point(0, 20), New Point(-20, 0)) Dim customCap As New CustomLineCap(Nothing, gp) pen.CustomEndCap = customCap g.DrawLine(pen, 50, 100, 60, 35) g.DrawLine(pen, 50, 150, 150, 100) g.DrawLine(pen, 50, 170, 150, 190) g.DrawLine(pen, 50, 180, 70, 250) g.SmoothingMode = SmoothingMode.AntiAlias ' the following lines will be smoothed out g.DrawLine(pen, 250, 100, 260, 35) g.DrawLine(pen, 250, 150, 350, 100) g.DrawLine(pen, 250, 170, 350, 190) g.DrawLine(pen, 250, 180, 270, 250) 
end example

Figure 4-6 shows the difference.

click to expand
Figure 4-6: Applying anti-aliasing

Rotation

The Graphics class allows you to rotate the drawing plane. You do this by using the RotateTransform method of the Graphics class. To return the plane to normal, use the ResetTransform method.

Listing 4-4 illustrates how rotation affects the drawing.

Listing 4-4: Applying Rotation

start example
 Dim g As Graphics = Me.CreateGraphics() Dim pen As New Pen(Color.Black, 1) Dim font As New Font("Courier", 12) Dim x As Integer = 120 Dim y As Integer = 20 g.DrawString("Advanced .NET Drawing", font, Brushes.Black, x, y) g.DrawLine(pen, x, y + 20, x + 180, y + 20) g.RotateTransform(30) g.DrawString("Advanced .NET Drawing", font, Brushes.Black, x, y) g.DrawLine(pen, x, y + 20, x + 180, y + 20) ' Rotate another 30 degrees g.RotateTransform(30) g.DrawString("Advanced .NET Drawing", font, Brushes.Black, x, y) g.DrawLine(pen, x, y + 20, x + 180, y + 20) 
end example

Figure 4-7 shows the result.

click to expand
Figure 4-7: Applying rotation

Translate Transform

By default, all the coordinates you pass to the Graphics class's drawing methods are relative to the origin—which is coordinate (0,0). You can, however, translate the origin to another point in the coordinate so that the coordinates passed to the drawing methods are measured relative to the new point.

Listing 4-5 moves the origin to the coordinate (200, 200). Anything drawn will be measured to be relative to (200, 200).

Listing 4-5: Applying Translation

start example
 Dim g As Graphics = Me.CreateGraphics() Dim pen As New Pen(Color.Black, 1) Dim font As New Font("Courier", 8) ' draw a circle at the center of rotation g.FillEllipse(Brushes.Red, 200, 200, 9, 9) g.TranslateTransform(200, 200) Dim i As Integer For i = 0 To 11   g.DrawString("Advanced .NET Drawing", font, Brushes.Black, 20, 20)   g.DrawLine(pen, 20, 35, 150, 35)   g.RotateTransform(30) Next 
end example

Figure 4-8 shows the result of this code.

click to expand
Figure 4-8: Applying translation




Real World. NET Applications
Real-World .NET Applications
ISBN: 1590590821
EAN: 2147483647
Year: 2005
Pages: 82

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net