Introduction to Graphics Programming Using WFC

Microsoft Corporation

September 1, 1998

The display of graphical objects in Microsoft® Windows® occurs through the graphics device interface (GDI), a device-independent graphics output model that processes graphical function calls from a Windows-based application and passes those calls to the appropriate device driver. The driver performs the hardware-specific functions that generate output. By acting as a buffer between applications and output devices, the GDI presents a device-independent view for the application while interacting in a device-dependent format with the device.

Application developers use the functionality of the GDI to display images, to draw controls, shapes, and text, and to create and use pens, brushes, and fonts. The Windows Foundation Classes (WFC) for Java Graphics object coordinates with other WFC objects, such as the Pen, Font, and Brush objects, to encapsulate these capabilities as Java-based objects.

In the WFC environment, graphical output occurs through the Graphics object. This article provides an illustrated overview of this object's capabilities, and includes the following topics:

Creating a Graphics Object
Graphics Object Scope
Maintaining the Bounding Rectangle
Drawing Text
Using Pens
Using Brushes
Drawing Bitmaps
Raster Operations
Drawing Shapes

Creating a Graphics Object

The WFC environment provides several ways to access the capabilities of the Graphics object.

  1. You can explicitly create a Graphics object from within a control class. To do this, use the form's createGraphics method, as follows:
    Graphics g = this.createGraphics();
    

    When you create a Graphics object in this fashion, graphical operations performed using that object apply only to the control through which it was created.

  2. You can add a paint event handler to your Form class code, and use the Graphics object instance stored in the PaintEvent object. To do this, display the form's properties in the Properties window, open the event list, and double-click the paint entry. This action adds the following handler in your Form class code.
    Form1_paint(Object sender, PaintEvent e){
    }
    

    The PaintEvent object contains a graphics public member, and this member is a valid Graphics object instance. Reference this object in your paint event handler as follows:

    Form1_paint(Object sender, PaintEvent e){
    e.graphics.drawString("Hello, World", new Point(10, 10));
    }
    
  3. You can retrieve a Graphics object instance from any object that supports a method that creates and returns a Graphics object. The Bitmap object's getGraphics method provides an example of this support.
    Bitmap bmpBubbles = new Bitmap("c:\\bubbles.bmp");
    Graphics g = bmpBubbles.getGraphics();
    

    Keep in mind that when you retrieve a Graphics object instance in this way, the object belongs to the object through which you retrieved it. Thus, in the example above, a call to one of the object's drawing methods results in drawing to the bitmap, not to the form.

After creating a Graphics object, you associate other graphics-based objects, such as fonts, pens, and brushes with the object, and then use the object's numerous drawing methods to render output to the display. For example, to draw lines with a specific appearance, you use the Graphics object's setPen method to specify the pen that the object will use for drawing. You can modify these associations as often as you want.

The following table lists the objects that you specifically associate with the Graphics object.

Object Description
Brush Used to fill enclosed surfaces with patterns, colors, or bitmaps.
Font Used to describe text to be rendered to a form.
Pen Used to draw lines and polygons, including rectangles, arcs, and pies.

Graphics Object Scope

The Graphics object has method scope. This means that when a method in which you use the Graphics object returns, the object's dispose method is called automatically, freeing all resources that the object has allocated. After dispose has been called, attempts to use the object result in a run-time exception.

If you declare an instance of the Graphics object at the class level, you should use the Form object's createGraphics method to initialize that object in every method that uses it.

Public class Form1 extends Form{
Graphics g = new Graphics();
Private void Form1_resize(Object sender, Event e){
// Initialize object instance.
g = this.createGraphics();
// dispose automatically called…
}
private void Form1_click(Object sender, Event e){
// Initialize object instance.
g = this.createGraphics();
// dispose method automatically called…
}
}

Maintaining the Bounding Rectangle

The area of a window in which you can draw is referred to as the window's client area. Within this area, a bounding rectangle defines the invisible rectangular region in which the Graphics object draws, and can include the window's entire client area.

When a window loses and then regains focus, the part of the bounding rectangle covered by some other object does not automatically redisplay the items that were previously rendered to it.

To ensure correct display, you must manage the repainting of your form. The paint event handler is the place in your Form class code in which such management typically takes place. Within this handler, you restore the bounding rectangle to its proper state.

The following example creates a Bitmap object at the class level, then uses the paint event handler to redraw the Bitmap. Each time the client area of the form becomes invalid, Windows invokes this handler, and the image is repainted to the form.

Bitmap bmp = new Bitmap("c:\\MyImage.bmp");
private Form1_paint(Object sender, PaintEvent e){
e.graphics.drawImage(bmp, new Point(0, 0));
}

When your form is initially displayed, and each time it regains focus, the paint event handler is automatically invoked. However, if your form supports resizing, a change in the form's dimensions does not automatically trigger a repaint. Instead, you must add a resize handler to the Form class and then call the Form object's invalidate method from within this handler. A call to invalidate trigger's the form's paint event handler.

private void Form1_resize(Object sender, Event e){
this.invalidate();
}

Drawing Text

The Graphics object's drawString method outputs a string of text to a form. The drawString method writes the text to the location that you specify, using the object's current background color, text color, and font settings.

The following call to the drawString method displays the text "Hello, World" in the upper left corner of a form.

Graphics g = this.createGraphics();
g.drawString("Hello, World", new Point(0, 0));

To determine the text and background color of the text, use the Graphics object's setBackColor and setTextColor before calling drawString.

// Set the text to white, the background to black.
g.setTextColor(Color.WHITE);
g.setBackColor(Color.BLACK);
// Draw the text...

The setBackColor method affects only the background color of text. To set the background or fill color for other objects, such as polygons and lines, use the setBrush and setPen methods, respectively.

Using the Font Object

A font is a collection of characters and symbols that share a common design. The major elements of a font include its typeface, style, and size.

In the WFC, Windows fonts are encapsulated in the Font object. The Graphics object supports the setFont method, which associates a font with the Graphics object. The object also supports the getFontDescriptors method, which returns an array of FontDescriptor objects, in which each object describes one of the available system fonts.

After you associate a Font with the Graphics object, all text drawn within the Graphics object's bounding rectangle is drawn using that font.

The following program statements illustrate the process of setting the background color of the Graphics object to white, the text color to black, and the font to 26-point Times New Roman. The text is drawn to the display at a specified position.

Graphics g = this.createGraphics();
g.setFont(new Font("Times New Roman", 26));
g.setBackColor(Color.WHITE);
g.setTextColor(Color.BLACK);
g.drawString("Hello, World", 0, 0);

Enumerating Fonts

In some instances, an application must be able to enumerate and retrieve detailed information about the available fonts, and to select the one most appropriate for a particular operation.

The WFC FontDescriptor object describes a font, including the font's name, height, orientation, and so forth. For a detailed description of all the fonts available on your system, use the Graphics object's getFontDescriptors method. This method returns an array of FontDescriptor objects, in which each element in the array describes a font.

The following example illustrates how to use the getFontDescriptors method. This example retrieves an array of available fonts and then inserts a unique list of the font names into a list box.

Graphics g = this.createGraphics();
// Create the array.
FontDescriptor rgFonts[] = g.getFontDescriptors();
for(int i = 0; i < rgFonts.length; i++){
if(listBox1.findString(rgFonts[i].fullName) == -1){
listBox1.addItem(rgFonts[i].fullName);
}
}

Using Pens

A pen is a graphics tool that an application for Microsoft Windows uses to draw lines and curves. Drawing applications use pens to draw freehand lines, straight lines, and curves. Computer-aided design (CAD) applications use pens to draw visible lines, hidden lines, section lines, center lines, and so on. Word processing and desktop publishing applications use pens to draw borders and rules. Spreadsheet applications use pens to designate trends in graphs and to outline bar graphs and pie charts.

Each pen consists of three attributes: style, width, and color. While no limits are imposed on the width and color of a pen, the pen's style must be supported by the operating system. These styles are illustrated in the following figure.

Figure 1. Pen styles

WFC Pen Object

The capabilities of Win32® pens are encapsulated in the WFC Pen object. The following code fragment demonstrates how to create a Pen object.

Pen p = new Pen(Pen.PS_DASH);

The constant that you pass to the Pen object constructor is a pen style. The seven built-in pen styles supported on Windows are each represented by a constant defined in the Pen class. The following table lists these constants.

Table 1. Pen style constants

Constant Description
Pen.STYLE_DASH Represents a dashed pen.
Pen.STYLE_DOT Represents a dotted pen.
Pen.STYLE_DASHDOT Represents a pen of alternating dashes and dots.
Pen.STYLE_DASHDOTDOT Represents a pen of alternating dashes and double dots.
Pen.STYLE_INSIDEFRAME Represents a pen that draws a line inside the frame of closed shapes produced by the Graphics object's output functions that specify a bounding rectangle (for example, drawRect, drawPie, and drawChord).
Pen.STYLE_NULL Represents a null pen.
Pen.STYLE_SOLID Represents a solid pen.

Setting the Pen on a Graphics Object

The Pen object itself contains no coloring or drawing capability. It only describes a subset of the capabilities of the GDI. Before you use a Pen to draw on a form, you associate the Pen with a Graphics object using the Graphics object's setPen method.

Graphics g = this.createGraphics();
g.setPen(new Pen(Pen.STYLE_DASH));

After you associate a Pen with a Graphics object, all lines drawn within the Graphics object's bounding rectangle are drawn using that pen. In addition, you can call the setPen method any number of times on the same Graphics object.

The following example demonstrates how the Pen object works with the line-drawing capabilities of the Graphics object. In this example, the Pen styles defined in the Pen object are stored in an array of integers. Within the class's paint event handler, a for loop is used to iterate through this array of pen styles, drawing a single line using each style.

public class Form1 extends Form{
// Create an array that contains all Pen styles.
int rgPenStyles[] = { Pen.STYLE_DASH, Pen.STYLE_DASHDOT, Pen.STYLE_DASHDOTDOT,
Pen.STYLE_DOT,Pen.STYLE_INSIDEFRAME, Pen.STYLE_SOLID };
// Loop through the array of PenStyles, associating each with the
// Graphics object in turn, and then use the drawLine method to
// draw a line that uses the pen.
private void Form1_paint(Object sender, PaintEvent e){
Rectangle rcBounds = this.getBounds();
rcBounds.y += 10;

for(int i = 0; i < rgPenStyles.length; i++){
e.graphics.setPen(new Pen(Color(0,0,0), rgPenStyles[i]));
e.graphics.drawLine(new Point(0, rcBounds.y), 
new Point(rcBounds.width, rcBounds.y));
rcBounds.y += 10;
}
}
// Rest of Form1 class...

Using Brushes

A brush is a graphics tool that a Win32-based application uses to paint the interior of polygons, ellipses, and paths. Drawing applications use brushes to paint shapes; word processing applications use brushes to paint rules; computer-aided design (CAD) applications use brushes to paint the interiors of cross-section views; and spreadsheet applications use brushes to paint the sections of pie charts and the bars in bar graphs.

There are two types of brushes: logical and physical. A logical brush is one that you define in code as the ideal combination of colors and/or pattern that an application should use to paint shapes. A physical brush is one that a device driver creates based on your logical-brush definition.

Brush Origin

When an application calls a drawing function to paint a shape, Windows positions a brush at the start of the paint operation and maps a pixel in the brush bitmap to the window origin of the client area. (The window origin is the upper-left corner of the window's client area.) The coordinates of the pixel that Windows maps are called the brush origin.

The default brush origin is located in the upper-left corner of the brush bitmap at the coordinates (0,0). Windows then copies the brush across the client area, forming a pattern that is as tall as the bitmap. The copy operation continues, row by row, until the entire client area is filled. However, the brush pattern is visible only within the boundaries of the specified shape. (Here, the term bitmap is used in its most literal sense—as an arrangement of bits—and does not refer exclusively to bits stored in an image file).

There are instances when the default brush origin should not be used. For example, it may be necessary for an application to use the same brush to paint the backgrounds of its parent and child windows and blend a child window's background with that of the parent window.

The following illustration shows a five-pointed star filled by using an application-defined brush. The illustration shows a zoomed image of the brush, as well as the location to which it was mapped at the beginning of the paint operation.

Figure 2. Brush origin

Logical Brush Types

Logical brushes come in three varieties: solid, pattern, and hatched.

A solid brush consists of a color or pattern defined by some element of the Windows user interface (for example, you can paint a shape with the color and pattern conventionally used by Windows to display disabled buttons).

A hatched brush consists of a combination of a color and of one of the six patterns defined by Win32. The following table illustrates the appearance of these predefined patterns.

Table 2. Hatched brush styles

Brush Style Illustration
Backward diagonal
Cross-hatched
Diagonally cross-hatched
Forward diagonal
Horizontal
Vertical

A pattern brush consists of a bitmap that is used as the basis for a pattern that fills a shape. Where the area to be filled is larger than the bitmap, the bitmap is tiled horizontally and vertically across the display. Pattern brushes enable you to create custom brushes that consist of any pattern that you define.

The WFC Brush Object

The capabilities of Win32 brushes are encapsulated in the WFC Brush object, an object that supports the creation of solid, pattern, and hatched brushes.

The Brush object represents these objects in various ways. For example, solid brushes are represented as a group of public static final members, each of which is itself a Brush object. Hatched brushes are represented as a group of integer constants, each of which represents a different brush style.

The following table lists the Brush object constants that represent solid brushes.

Table 3. Solid brush object constants

Constant Description
Brush.BLACK Represents a solid black brush.
Brush.DARKGRAY Represents a dark gray brush.
Brush.HALFTONE Represents a halftone brush.
Brush.HOLLOW Represents a transparent brush.
Brush.LIGHTGRAY Represents a light gray brush.
Brush.NULL Represents a null brush.
Brush.WHITE Represents a white brush.

The following table lists the Brush object constants that represent hatched brushes.

Table 4. Hatched brush object constants

Constant Description
Brush.STYLE_BDIAGONAL Represents a backward diagonal brush. Parallel lines run from the lower-left corner of the brush origin to the upper-right corner.
Brush.STYLE_DIAGACROSS Represents a cross-hatched brush.
Brush.STYLE_FDIAGONAL Represents a forward diagonal brush. Parallel lines run from the lower-right corner of the brush origin to the upper-left corner.
Brush.STYLE_HORIZONTAL Represents pattern consisting of evenly spaced horizontal lines.
Brush.STYLE_VERTICAL Represents pattern consisting of evenly spaced vertical lines.

Creating a Brush Object

How you create a Brush object depends on the type of brush needed. Because solid brushes are Brush objects, you create a solid brush as follows:

Brush br = Brush.BLACK;

A pattern brush is represented by the Brush object as an integer constant and is created as follows:

Brush br = new Brush(new Color(0,0,0), Brush.STYLE_FDIAGONAL);

The following code fragment demonstrates one way to create a brush based on a bitmap:

Brush bmpBrush = new Brush(new Bitmap("c:\\myBitmap.bmp"));

This example assumes that the bitmap on which you want to base your brush pattern is stored in a file on disk. However, you can also use the Bitmap object's createBitmap method to define bitmaps at run time, and can use the bitmap you define as the basis for the brush pattern.

Setting the Brush on the Graphics Object

Like the Pen and Font objects, the Brush object contains no coloring or drawing capability. It expresses a subset of the capabilities of the GDI. Before a brush can be used to fill surfaces, it must be associated with a Graphics object using the setBrush method.

// Declare class-level Brush variable.
Brush br = new Brush(Brush.STYLE_FDIAGONAL);
public void Form1_paint(Object sender, PaintEvent e)
{
// Associate the brush with the Graphics object.
e.graphics.setBrush(Color.BLACK, br);
}

After you associate a Brush object with the Graphics object, all polygons that you draw using that Graphics object instance are filled with the associated brush. You can call setBrush as often as you want to associate a new Brush with the Graphics object.

Drawing Bitmaps

A bitmap is a drawable surface. This surface can be used to display pens, brushes, or images, including Windows bitmaps and .gif and .jpg images.

The Bitmap object supports creating and loading bitmaps. After creating a Bitmap object, you use the Graphics object's drawImage method to render a Bitmap object to the display.

The following code fragment creates a new Bitmap object and then uses the Form class's createGraphics method to create the Graphics object for drawing the image.

Bitmap bmp = new Bitmap("c:\\MyImage.bmp");
Graphics g = this.createGraphics();
g.drawImage(bmp, new Point(10, 10));

Raster Operations

A raster operation applies a logical operation to the display of a GDI primitive, such as a pen, brush, image, or shape, to achieve a visual effect. Raster operations that you can perform using the Graphics object are defined in the RasterOp object. When you review the methods supported by the Graphics object, and you'll see that for each basic operation, such as the drawing of lines or the display of images, there's a method that takes a RasterOp as a parameter.

In their simplest incarnations, the drawing methods the Graphics object supports merely write or copy pixels to some area of the display, overwriting what's currently displayed. Add raster operations to the equation, and this overwrite becomes more complex.

Suppose, for example, that you want to draw a black rectangle to an area currently covered by an image, but want to logically combine the black pixels with their corresponding pixels in the target image and to write the result to the display. Raster operations make such combinations possible.

The variations on the logic the RasterOps object supports are too numerous to be covered thoroughly in this document. The following statements, however, demonstrate the basic syntax of a call using a RasterOps object. These statements set the background color of a form, and then draw a line that represents a color inversion of the background color.

private void Form1_paint(Object sender, PaintEvent e)
{
this.setBackColor(new Color(255, 255, 255);
e.graphics.drawLine(new Point(10, 10), new Point(100, 10), 
RasterOp.TARGET.invert());
}

Drawing Shapes

The Graphics object supports the drawing of rectangles, rounded rectangles, arcs, Bezier splines, chords, and pies.

Lines

A line is a set of highlighted pixels on a display, identified by two points: a starting point and an ending point. In Windows, the pixel located at the starting point is always included in the line, and the pixel located at the ending point is always excluded (these lines are sometimes called inclusive-exclusive).

An application can draw a single line by calling the Graphics object's drawLine method. This method takes two Point parameters, which specify the start and end of the line.

private void Form1_paint(Object sender, Event e){
Rectangle rcClient = this.getClientRect();
// Draw lines that divide the screen area equally into four squares.
e.graphics.drawLine(new Point(rcClient.x, rcClient.height / 2),
new Point(rcClient.width, rcClient.height / 2));
e.graphics.drawLine(new Point(rcClient.width / 2, rcClient.y),
new Point(rcClient.width / 2, rcClient.height));
}

To draw a line from the current pen position to another point on the display, applications use the drawLineTo method, which takes a destination point as a parameter. For an example of how to use this method, see the Arcs section.

Rectangles

Applications written for Microsoft Windows use rectangles to specify rectangular areas on the screen or in a window. Rectangles are used to describe the client area of a window, areas of the screen that need repaints, and areas for displaying formatted text. Your applications can also use rectangles to fill, frame, or invert a portion of the client area with a given brush, and to retrieve the coordinates of a window or the window's client area.

The dimensions of rectangular regions are described in the WFC Rectangle object. This object consists of x, y, height, and width integers that describe the Rectangle's screen position and dimensions. In addition, you can use the object's getRight and getBottom methods to retrieve the screen position of its right and bottom sides.

Rectangle Operations

The Rectangle object provides a number of methods for working with rectangles. The object's equals method determines whether two Rectangle objects are identical—that is, whether they have the same coordinates.

The inflateRect method increases or decreases the width or height, or both, of a Rectangle. It can add or remove width from both ends of the Rectangle; it can also add or remove height from both the top and bottom of the Rectangle.

The overloaded contains method enables you to determine whether the area described by one Rectangle exists within the area described by another, or to determine whether a given point exists within a Rectangle.

The intersects and intersectsWith methods determine whether two Rectangle object's intersect.

A Rectangle Example

The example in this section illustrates how to divide areas of an application's client area into subrectangles, and how to work within these regions.

When the application starts, it divides the main form's client area into 16 by 16 sub-regions, and displays every shade of a given color within those rectangular regions. To modify the number of rectangles displayed across and down the display, you use the Dimensions menu item to display a dialog box in which you specify the new number of Rectangles you'd like to see displayed.

Figure 3. Rectangles example

The method that divides the screen into Rectangle objects is given in the following example. This method, getRects, takes three parameters: a Rectangle that specifies the area to be divided, and two integers, which identify the number of cells to draw across and down, respectively. The method returns an array of Rectangles that contain the appropriate coordinates.

private Rectangle[] getRects(Rectangle rcClient, int nDown, int nAcross){
int deltaX, deltaY; // The height and width of each cell.
int x, y;
int i;
Rectangle rgRects[] = new Rectangle[nDown * nAcross];
// Determine the height and width of each Rectangle.
deltaX = (rcClient.getRight() – rcClient.x) / nAcross;
deltaY = (rcClient.getBottom() – rcClient.y) / nDown;
// Create and initialize the Rectangle array.
for(y = rcClient.y, i = 0; y < rcClient.getBottom(); y += deltaY){
for(x = rcClient.x; x < (rcClient.getRight() – nAcross) &&
i < (nAcross * nDown); x += deltaX, i++){
rgRects[i] = new Rectangle(x, y, deltaX, deltaY);
}
}
// Return the initialized array.
return rgRects;
}

When the application starts, its Form class constructor initializes two class-level integers, nAcross and nDown, to 16, and calls the getRects method previously listed.

public Form1(){
initForm();
nAcross = 16;
nDown = 16;
// Initialize class-level array.
rgRects = getRects(this.getClientRect(), nAcross, nDown);
}

After the class constructor returns, the class's paint event handler is automatically called. This event handler loops through the class-level rgRects array, uses the Graphics object's setBrush method to set a new color, and then calls the drawRect method to draw the array's Rectangle.

private void Form1_paint(Object sender, PaintEvent e){
for(int i = 0; i < rgRects.length; i++){
e.graphics.setBrush(new Brush(new Color(0,0,i)));
e.graphics.drawRect(rgRects[i]);
}
}

Finally, the Form class's resize handler simply reinitializes the array and forces a repaint of the form's client area.

private void Form1_resize(Object sender, Event e){

Rectangle rcClient = this.getClientRect();
rgRects = getRects(rcClient, nDown, nAcross);
this.invalidate();
}

Chords

A chord is a region bounded by the intersection of an ellipse and a line segment, called a secant. The chord is outlined by using the current pen and filled by using the current brush. The part of the ellipse on one side of the secant is clipped.

Figure 4. Chord and secant

To draw a chord, use the Graphics object's drawChord method, which has the following syntax:

drawChord(Rectangle p1, Point p2, point p3);

The drawChord method's Rectangle parameter designates the area into which the ellipse will be draw. The two Point parameters identify the points at which the two ends of the secant intersect the ellipse.

The following sample makes two calls to drawChord. The first call draws a chord in the lower-left corner of the display, and the second draws a chord in the upper right corner. Prior to each call to drawChord, a different brush is associated with the Graphics object to ensure that the two chords, which combine to form a circle, are painted black and white, respectively.

private void Form1_paint(Object sender, PaintEvent e){
Rectangle rcClient = this.getClientRect();
// Associate a black brush with the object, and draw a chord.
e.graphics.setBrush(new Brush(new Color(0,0,0)));
e.graphics.drawChord(rcClient, new Point(rcClient.x, rcClient.y),
new Point(rcClient.getRight(), rcClient.getBottom());
// Associate a white brush with the object, and draw a chord.
e.graphics.setBrush(new Brush(new Color(255,255,255)));
e.graphics.drawChord(rcClient, new Point(rcClient.getRight(),
rcClient.getBottom()), new Point(rcClient.x, rcClient.y));
}

Arcs

An application can draw an ellipse or part of an ellipse by calling the drawArc method. This method draws a curve within the perimeter of an invisible rectangle called a bounding rectangle. The size of the ellipse is specified by two invisible radials extending from the center of the rectangle to the size of the rectangle. The following illustration shows an arc (part of an ellipse) drawn using the drawArc method.

Figure 5. Arc

When calling the drawArc method, an application specifies the coordinates of the bounding rectangle and radials. The preceding illustration shows the rectangle and radials with dashed lines while the actual arc was drawn using a solid line.

The following example draws an arc that fills the client area of a form, and then uses the Graphics object's drawLine method to draw a line from the top of the arc to its radius, then from its radius to the rightmost side of the ellipse.

void private Form1_paint(Object sender, Event e){
Rectangle rcClient = this.getClientRect(); 
e.graphics.drawArc(rcClient, new Point(rcClient.width /2,rcClient.y), 
new Point(rcClient.width, rcClient.height / 2));
e.graphics.drawLine(new Point(rcClient.width / 2, rcClient.y),
new Point(rcClient.width / 2, rcClient.height / 2));
e.graphics.drawLine(new Point(rcClient.width, rcClient.height / 2),
new Point(rcClient.width / 2, rcClient.height / 2));
}

ArcAngles

An arc angle is similar to an arc. The primary practical difference between the two is that when an application uses drawArc to draw an arc, it specifies the x and y locations of the arc's radials.

In contrast, to draw an arc angle, the application uses the drawArcAngle method, and specifies the degrees of the angle; the method itself takes care of locating the coordinates at which drawing starts and stops.

The drawArcAngle method has the following syntax:

public final void drawAngleArc(Point center, int radius, float startAngle, float endAngle)

The Point parameter identifies the screen location at which to place the radius, specified in the radius parameter. The startAngle specifies the number, in degrees, where the angle starts, and the endAngle parameter specifies how many degrees to draw, beginning at the startAngle.

The following example shows how to use this method. This method draws an arc angle, beginning at a start angle of 30, and extending 300 degrees from the start angle.

private void Form1_paint(Object sender, PaintEvent e){
Rectangle rcClient = this.getClientRect();
int x = rcClient.width / 2;
int y = rcClient.height / 2;
int radius = 100;
float startAngle = 30;
float endAngle = 300;
e.graphics.drawLineTo(new Point(x,y)); 
e.graphics.drawAngleArc(new Point(x,y), radius,startAngle, endAngle); 
e.graphics.drawLineTo(new Point(x,y)); 
} 

Code Listings

The following is a complete code listing for the Rectangle example referenced earlier in this chapter.

// Form1.java

import com.ms.wfc.app.*;
import com.ms.wfc.core.*;
import com.ms.wfc.ui.*;

/**
* This class can take a variable number of parameters on the command
* line. Program execution begins with the main() method. The class
* constructor is not invoked unless an object of type 'Form1'
* created in the main() method.
*/
public class Form1 extends Form
{

private void Form1_resize(Object sender, Event e)
{
rgRects = getRects(this.getClientRect(), nDown, nAcross);
this.invalidate();

}

public int nAcross, nDown;
Rectangle rgRects[];

private void Form1_paint(Object sender, PaintEvent e)
{

for(int i = 0; i < rgRects.length; i++){

e.graphics.setBrush(new Brush(new Color(0,0,i)));
e.graphics.drawRect(rgRects[i]);
}

}


private void mnuDimensions_click(Object sender, Event e)
{
Dimensions d = new Dimensions();
this.addOwnedForm(d);
d.showDialog();

rgRects = getRects(this.getClientRect(), nAcross, nDown);
this.invalidate();

}

public Form1()
{
// Required for Visual J++ Form Designer support.
initForm();
nAcross = 16;
nDown = 16;

rgRects = getRects(this.getClientRect(), nAcross, nDown);

// TODO: Add any constructor code after initForm call.
}

private Rectangle[] getRects(Rectangle rcClient, int nDown,
int nAcross){

int deltaX, deltaY;
int x,y;
int i;

Rectangle rgRects[] = new Rectangle[nDown * nAcross];

deltaX = (rcClient.getRight() - rcClient.x) / nAcross;
deltaY = (rcClient.getBottom() - rcClient.y) / nDown;

// Initialize the array of cells.

for(y = rcClient.y, i = 0; y < rcClient.getBottom(); y += deltaY){

for(x = rcClient.x; x < (rcClient.getRight() - nAcross) &&
i < (nAcross * nDown); x += deltaX, i++){

rgRects[i] = new Rectangle(x, y, deltaX, deltaY);
}
}

return rgRects;
}


/**
* The main entry point for the application.
*
* @param args Array of parameters passed to the application
* via the command line.
*/
public static void main(String args[])
{
Application.run(new Form1());
}


/**
* NOTE: The following code is required by the Visual J++ form
* designer. It can be modified using the form editor. Do not
* modify it using the code editor.
*/

Container components = new Container();
MainMenu mainMenu1 = new MainMenu();
MenuItem menuItem1 = new MenuItem();
MenuItem mnuDimensions = new MenuItem();

private void initForm()
{

mnuDimensions.setText("&Dimensions..");
mnuDimensions.addOnClick(new EventHandler(this.mnuDimensions_click));
menuItem1.setMenuItems(new MenuItem[] {
mnuDimensions});
menuItem1.setText("&Rectangle");
mainMenu1.setMenuItems(new MenuItem[] {
menuItem1});
/* @designTimeOnly mainMenu1.setLocation(new Point(0, 0)); */
this.setBackColor(Color.CONTROL);
this.setLocation(new Point(0, 0));
this.setSize(new Point(368, 320));
this.setTabIndex(-1);
this.setTabStop(true);
this.setText("Rectangles Example");
this.setAutoScaleBaseSize(13);
this.setClientSize(new Point(360, 273));
this.setMenu(mainMenu1);
this.addOnResize(new EventHandler(this.Form1_resize));
this.addOnPaint(new PaintEventHandler(this.Form1_paint));
}
// NOTE: End of form designer support code.

public static class ClassInfo extends Form.ClassInfo
{
// TODO: Add your property and event infos here.
}
}

The following is the code listing for Dimensions.java, the dialog through which the user specifies the number of rectangles displayed down and across the application's main form.

// Dimensions.java

import com.ms.wfc.app.*;
import com.ms.wfc.core.*;
import com.ms.wfc.ui.*;

/**
* This class can take a variable number of parameters on the command
* line. Program execution begins with the main() method. The class
* constructor is not invoked unless an object of type 'Dimensions'
* created in the main() method.
*/
public class Dimensions extends Form
{
private void btnOK_click(Object sender, Event e)
{

Integer nAcross, nDown;

if(txtAcross.getText().length() > 0 & txtAcross.getText().length() > 0){

nAcross = new Integer(txtAcross.getText());
nDown = new Integer(txtDown.getText());



if(nAcross.intValue() > 0 && nAcross.intValue() < 16){
if(nDown.intValue() > 0 && nDown.intValue() < 16){ 

Form1 frm = (Form1)this.getOwner();

frm.nAcross = nAcross.intValue();
frm.nDown = nDown.intValue();
}
}
}

this.dispose();
}

public Dimensions()
{
// Required for Visual J++ Form Designer support.
initForm(); 

// TODO: Add any constructor code after initForm call.
}

/**
* The main entry point for the application. 
*
* @param args Array of parameters passed to the application
* via the command line.
*/
public static void main(String args[])
{
Application.run(new Dimensions());
}


/**
* NOTE: The following code is required by the Visual J++ form
* designer. It can be modified using the form editor. Do not
* modify it using the code editor.
*/

Container components = new Container();
Label label1 = new Label();
Label label2 = new Label();
Edit txtAcross = new Edit();
Edit txtDown = new Edit();
Button btnOK = new Button();
Button btnCancel = new Button();

private void initForm()
{

this.setBackColor(Color.CONTROL);
this.setLocation(new Point(0, 0));
this.setSize(new Point(300, 134));
this.setTabIndex(-1);
this.setTabStop(true);
this.setText("Dimensions");
this.setAutoScaleBaseSize(13);
this.setClientSize(new Point(292, 107));
label1.setLocation(new Point(10, 10));
label1.setSize(new Point(100, 20));
label1.setText("Rectangles Across:");
label1.setTabIndex(0);
label2.setLocation(new Point(10, 40));
label2.setSize(new Point(90, 20));
label2.setText("Rectangles Down:");
label2.setTabIndex(1);
label2.setTextAlign(HorizontalAlignment.RIGHT);
txtAcross.setBackColor(Color.WINDOW);
txtAcross.setCursor(Cursor.IBEAM);
txtAcross.setLocation(new Point(110, 10));
txtAcross.setSize(new Point(90, 20));
txtAcross.setTabIndex(2);
txtAcross.setTabStop(true);
txtAcross.setText("");
txtDown.setBackColor(Color.WINDOW);
txtDown.setCursor(Cursor.IBEAM);
txtDown.setLocation(new Point(110, 40));
txtDown.setSize(new Point(90, 20));
txtDown.setTabIndex(3);
txtDown.setTabStop(true);
txtDown.setText("");
btnOK.setLocation(new Point(70, 80));
btnOK.setSize(new Point(60, 20));
btnOK.setTabIndex(4);
btnOK.setTabStop(true);
btnOK.setText("OK");
btnOK.addOnClick(new EventHandler(this.btnOK_click));
btnCancel.setLocation(new Point(140, 80));
btnCancel.setSize(new Point(70, 20));
btnCancel.setTabIndex(6);
btnCancel.setTabStop(true);
btnCancel.setText("Cancel");
this.setNewControls(new Control[] {
btnCancel, 
btnOK, 
txtDown, 
txtAcross, 
label2, 
label1});
}
// NOTE: End of form designer support code.

public static class ClassInfo extends Form.ClassInfo
{
// TODO: Add your property and event infos here.
}
}