Docsity
Docsity

Prepare for your exams
Prepare for your exams

Study with the several resources on Docsity


Earn points to download
Earn points to download

Earn points by helping other students or get them with a premium plan


Guidelines and tips
Guidelines and tips

Graphics Programming with C#: Creating Lines, Shapes, and Menus, Study notes of Computer Science

An introduction to graphics programming using c# language. It covers the basics of drawing lines, creating menus, and handling user input. The document also includes examples of creating a main menu and handling different shapes like lines, rectangles, and ellipses.

Typology: Study notes

Pre 2010

Uploaded on 08/07/2009

koofers-user-k6t
koofers-user-k6t 🇺🇸

10 documents

1 / 21

Toggle sidebar

Related documents


Partial preview of the text

Download Graphics Programming with C#: Creating Lines, Shapes, and Menus and more Study notes Computer Science in PDF only on Docsity!

8-2 Introduction to Computer Programming Using C#

Graphics Concepts in Windows

One of the most attractive features of graphical user interfaces, like Windows, is the elegant handling of graphics in a simple, device independent manner. Prior to the introduction of these environments, the programmer needed to worry about the low-level hardware characteristics of the video adapter and printer that might be attached to a user's computer. Any time a new piece of equipment hit the market, the software developers had to rush out with new device drivers so their products could support the new hardware. Under Windows, those days are just an unpleasant memory. Output devices are treated in a generic manner. If a programmer develops an application that works in this generic environment, it will behave reasonably in Windows, regardless of the particular characteristics of the output device.

To make matters even better, C# makes it relatively easy to gain access to the graphics features embedded in Windows. In this section, we'll take a brief look at some of the C# facilities for using graphics. In addition, we’ll take a quick look at creating classes to simplify utilization of those facilities.

Basic Graphics Concepts in C#

The simplest graphical elements in C# are generated with a Pen and a Brush by “drawing” on a Graphics variable that has been associated with a C# control. In their simplest form, the Pen and Brush are graphical tools for outlining and filling regions, respectively, with a particular Color. The only real customization we’ll apply to the Pen element will be specification of a Width attribute. For brushes, we’ll only use solid brushes of a single color. It should be noted, though, that the programmer can customize these tools in a variety of ways to produce a variety of complex images.

To create a Graphics variable that is to be associated with the current form, you first need to declare a variable; out of laziness, more than anything else, programmers seem to like naming this graphics variable g :

Graphics g;

Normally, you defer assigning a value to this variable until you’re ready to use it, and enclosing the creation and use of the variable in a using block:

using (g = this.CreateGraphics()) { ... // instructions to draw on g }

This particular version associates the variable g with the current form. If you instead wanted to “draw” on one of the controls that’s on the form, you can instead invoke the CreateGraphics() function for that control:

Basic Graphics and Mouse Management Concepts 8-

using (g = Panel1.CreateGraphics()) ...

The using block ensures that any errors that might occur are trapped, and that any resources used in the drawing process are properly freed. Virtually all graphics operations should be performed inside a using block.

Before examining the specific details of graphical operations in C#, it is also necessary to consider one further concept - the addresses of points associated with a Graphics variable. The upper left corner of the variable has the ( x , y ) coordinates (0,0), with these addresses increasing from left to right and from top to bottom.

As noted above, when pens and brushes are created, a color should be associated with the brush. You can store the setting for a color in a variable of type Color , and can most readily set a color by using one of the built-in names for colors. To obtain a list of these built-in names, click on the pull-down tab available in the BackColor attribute of a form, and prefixing that name with Color. For example, Color.Black is a convenient way to specify the color black.

A simple pen might be created and used like this:

Pen p;

using (p = new Pen (Color.Black)) ...

To create a pen with a width greater than 1, instead use this form:

Pen p;

using (p = new Pen (Color.Black, width)) ...

Similarly, a solid black brush can be created and used like this:

Brush b;

using (b = new SolidBrush (Color.Black)) ...

Drawing Lines and Simple Shapes in C#

Once a pen and/or brush have been created, it’s very easy to use those items to draw on the Graphics component. For example, to draw a red line from position (20, 40) to position (100,

  1. on the form with a pen that is 3 units wide, you could use the instruction sequence:

using (g = this.CreateGraphics()) using (pen = new Pen (Color.Red, 3)) g.DrawLine (pen, 20, 40, 100, 120);

8-4 Introduction to Computer Programming Using C#

Of course, if you wanted to draw a series of lines, you could enclose the series of DrawLine requests in braces.

Similarly, an ellipse or rectangle can be drawn by specifying its brush (for a filled shape) or pen (for an outlined shape), the coordinates of the upper left-hand corner of the rectangle in which the shape is to be drawn, and the width and height of that bounding rectangle. For example, a filled rectangle, with the upper left corner at position (20, 40) and the lower right corner at position (100, 120) could be drawn like this:

g.FillEllipse(brush, 20, 40, (100 – 20), (120 – 40));

An outlined rectangle could similarly be drawn by:

g.DrawRectangle(pen, 20, 40, (100 – 20), (120 – 40));

If you want a shape to be filled with one color, and then outlined with another, you should first draw the filled shape, and then draw the outlined shape; reversing the order will cause the border to be broken up and jagged. Also, be sure that the width and height parameters are positive.

Basic Mouse Management

Every time we do anything with the mouse - click or release a button, or even move the mouse to a new location - an "event" is generated. If the mouse is positioned over one of the controls on the form, then the mouse event is "owned" by that control. When the mouse is over a region of the form that contains no other controls, however, the event is “owned” by the underlying form.

If you look at the list of events associated with the form (remember – click the lightning bolt on the Properties window), you'll find several mouse-related events, including the following:

MouseDown : Generated whenever one of the mouse's buttons is pressed MouseUp : Generated whenever one of the mouse's buttons is released MouseMove : Generated whenever the mouse is moved

The event handlers for these events all have the same header format:

private void Form1_MouseXXXX(object sender, MouseEventArgs e)

The parameters that are conveyed to the handler includes sender , the identity of the control that detected the mouse action (this is often ignored), and the more important, and useful, MouseEventsArg parameter e , which tells you where the mouse is located, and which mouse button is being pressed. For example, within the event handler, the position of the mouse can be determined by referencing e.X and e.Y , while testing to determine which button is being pressed can be determined by a comparison:

if (e.Button == MouseButtons.Left) ...

Basic Graphics and Mouse Management Concepts 8-

Other choices for the button include MouseButtons.Center and MouseButtons.Right.

To illustrate how the information conveyed to a mouse-related event handler can be used, let's look at a couple of elementary examples:

a) Draw a line from the upper left corner of the screen to the current mouse position:

g.DrawLine (pen, 0, 0, e.X, e.Y);

b) If the left mouse button is involved, draw a line from (0,0) to the current position:

if (e.Button == MouseButtons.Left) g.DrawLine (pen, 0, 0, e.X, e.Y);

Creating a Main Menu

Many (perhaps most) Windows applications contain a main menu. A main menu is normally positioned at the top of the program's main window, attached to the title bar; they are sometimes found at the top of one or more of the program's child windows, as well.

To create a main menu, select the Menu Strip control and drop it on the form; the Visual Studio’s design window look approximately like this:

8-6 Introduction to Computer Programming Using C#

The Menu Strip control doesn’t have a visible presence on the form; instead, you can click on the entry below the form window to edit the contents of the menu. The prompt on the form to “Type Here” is inviting you to enter the text that you’d like to appear for that particular menu entry. If you click the mouse in that cell, the screen will change, like this:

As you can probably guess, then, creating a menu is really just a matter of filling in the entries, both on the primary “menu strip,” and also on the pull-down strips below each entry.

Responding to Menu Clicks

When your program is running, menus and submenus are displayed automatically, in response to mouse activities. All that you need to do is provide event handlers that specify how you want to respond to the various clicks. To associate a Click event handler with a menu entry, just double- click on that entry on the form, and the event handler will be created. For example, if you were to type Testing in one of the menu cells, and then double-click that cell, an event handler for that cell, similar to the following, will be created:

private void testingToolStripMenuItem_Click(object sender, EventArgs e) {

}

You can then enter whatever instructions are appropriate for responding to the click.

Basic Graphics and Mouse Management Concepts 8-

Using the Windows Color Dialog Tool

As noted earlier, pens and brushes have a color attribute; if you know the “name” of the color that you want to use, you can provide it directly. Quite often, though, an application may need to prompt the keyboard user to select a color. For such an instance, C# allows you to use the standard color dialog box that is a part of Windows.

The first step is to select the Color Dialog tool from the tool box, and drop it onto your form. Like the menu, the Color Dialog doesn’t have a visual component directly on the form. Instead, you again need to use the entry below the form in the design window to access the dialog.

There really aren’t very many attributes of the Color Dialog that can be customized. The AllowFullOpen attribute can be set to either true or false ; when set to true , the keyboard user can either select one of the displayed color choices, or open the “ Define Custom Color ” portion of the dialog to have more flexibility in defining a color. When set to false , only the primary portion of the color selection dialog can be displayed.

The Color attribute can be set to specify the color that should be highlighted when the dialog box is displayed, and will also contain the color that was chosen by the keyboard user. A typical usage of the Color Dialog box might be something like this:

if (colorDialog1.ShowDialog() == DialogResult.OK) { pen = new Pen(colorDialog1.Color); ... }

As you might surmise from the above, the Color Dialog box will return the identity of the button that was used to close the box. The above code fragment tests to ensure that the OK button was selected before using the selected color to create a pen.

Tracking Drawing History to Handle Paint Requests

There are two different models for screen management in a graphical user interface. The more obvious model states that whenever an application covers up a portion of the screen, that application is responsible for restoring the screen to its previous state. Unfortunately, though, this could be problematic if the “hidden” portion of the screen was being changed by an application that is running in the background. Therefore, Windows, and most graphical interface environments, use an alternate model. Whenever an application covers a portion of the screen, it is responsible for notifying the previous application when it subsequently releases the screen. Under Windows, whenever an application needs to restore all or part of the screen, a paint request is generated. Paint requests are also used to handle window re-size events, as well.

An application’s form, and any of the standard controls placed on that form, will automatically handle their own repaint needs. However, if your application is drawing directly on the screen,

8-8 Introduction to Computer Programming Using C#

then you need to take charge of repainting, as well. If your application’s drawing activity is fairly simple, this isn’t too big of a challenge. All you need to do is select the Paint event from the list of available form events, and then add instructions to the handler to reconstruct the screen.

For an application that is generating a more sophisticated image, or an image that may be at least under partial control of the keyboard user, you’ll need to maintain a complete record of the screen details. Fortunately, C# provides two tools that can greatly simplify this task.

The first of these tools is the class. Actually, we’ve been using classes all along – remember that the form itself is a class. The class is a fundamental building block of most modern programming languages, and is a combination of data items and functions for operating on those data items. In the context of the repainting process, we can use a class to keep track of the information needed to draw each piece of the screen image. In fact, if we go one step further and provide a drawing function as an element of the class, we can allow each screen component to paint itself onto the screen, as needed. We’ll discuss the design of a simple class in the next section.

The second tool we’ll be using is the ArrayList. This is a special variable that can be used to hold class variables, and we’ll use it to hold the individual screen components. To use an ArrayList , you must first add the directive:

using System.Collections;

to the set of using directives at the beginning of your program.

For the remainder of this discussion, assume that we’ve created a class named ScreenElement that describes the individual components that can be placed on the screen. To create an ArrayList variable to hold the actual ScreenElement variables, you’d need a declaration like the following at the start of your form definition:

ArrayList screenItems = new ArrayList();

As each screen component is created, a variable of type ScreenElement will need to be created. If this variable is named nextComponent , then it can be added to the screenItems list with the instruction: screenItems.add(nextComponent);

Now, whenever a repaint request generated, the following function could be used to restore the screen. The “skeleton” for this function was created by selecting the Paint event handler from the form’s event handler window.

private void Form1_Paint (object sender, PaintEventArgs e) { using (Graphics g = this.CreateGraphics()) foreach (ScreenElement s in screenItems) s.Draw(g); }

Basic Graphics and Mouse Management Concepts 8-

The foreach is another of C#’s repetition instructions; in this context, the instruction is used to cycle through the set of all screenItems , pulling them out and re-drawing them one by one.

The final detail that needs to be covered is removing items from the ArrayList variable. To clear the entire list, you can use the instruction:

screenItems.Clear();

Alternatively, to remove a specific element from the list, you can use the instruction:

ScreenElement x = whatever; ... screenItems.Remove(x);

This will remove the first occurrence of the specified item from the ArrayList.

Introduction to Simple Classes

As noted in the previous section, a class is a definition of a structure that contains both data items and functions for manipulating those items. Rather than examine classes in great detail, though, let’s just take a quick look at a class that might be useful for managing screen components. For simplicity, imagine that a screen layout consists of a combination of lines and circles. For each line, we need to track the end points, along with the color of the pen used to draw the line. For each circle, we’ll track the color of the brush, as well as the upper left and lower right corners of the box containing the circle.

We begin by declaring the data components of our class, which we’ll name ScreenElement :

class ScreenElement { public enum Shape {LINE, CIRCLE}; int x1, y1, x2, y2; Color shapeColor; Shape elementKind;

The only really new thing here is the first entry - public enum Shape. This tells the “rest of the world” that a Shape refers to either a Line or a Circle. This, in turn, will allow the program that is making use of the ScreenElement class to define what kind of shape the element represents.

The next item in the class is the constructor. This is a function that is used to create and initialize a variable of type ScreenElement , and looks like this:

public ScreenElement (Shape kind, int firstX, int firstY, int secondX, int secondY, Color c)

8-10 Introduction to Computer Programming Using C#

{

elementKind = kind; if (kind == Shape.LINE) { x1 = firstX; y1 = firstY; x2 = secondX; y2 = secondY; } else { if (firstX < secondX) { x1 = firstX; x2 = secondX – firstX; } else { x1 = secondX; x2 = firstX – secondX; } if (firstY < secondY) { y1 = firstY; y2 = secondY – firstY; } else { y1 = secondY; y2 = firstY – secondY; } shapeColor = c; }

I realize that this seems a bit complicated, but remember – for a line, you need only specify the two endpoints. For rectangles or ellipses, though, you need to specify the upper left corner of the enclosing rectangle, along with the width and the height. Therefore, when I stored the information for a line, I just stored the endpoints of the line. For a circle, though, I instead made sure that the first point was the upper left corner, and the second “point” was actually converted to the width and height of the enclosing square.

Now that the program knows how to create a ScreenElement , we’ll turn our attention to the other task associated with a ScreenElement – that of drawing the component to a graphics element. For this, we’ll use a function named draw, which has a single parameter – the Graphics “handle” to the control on which the element is to be drawn. The implementation of this function is straightforward:

public void draw(Graphics g) { Brush b; Pen p; if (shape == Shape.CIRCLE) {

Basic Graphics and Mouse Management Concepts 8-

using (b = new SolidBrush(shapeColor)) g.FillEllipse(b, b, x1, y1, x2, y2); } else { using (p = new Pen(penColor)) g.DrawLine(p, x1, y1, x2, y2); } }

With this class definition in place, we now have the missing piece that will allow us to fully implement the Paint event handler. As each new screen element is constructed, your program needs to create a variable of type ScreenElement to hold the details of that component, and add it to the collection of all ScreenElement variables, as described earlier. For example, if your program needs to create a blue line, with one endpoint at position ( startX,startY ), and the other endpoint at ( endX,endY ), you might use the instruction:

ScreenElement nextElement; ... nextElement = new ScreenElement(Shape.LINE, startX, startY, endX, endY, Color.Blue); ScreenItems.Add(nextElement);

Similarly, a red circle, centered at ( midX , midY ), with radius size , might be created like this:

ScreenElement nextElement; ... nextElement = new ScreenElement(Shape.CIRCLE, midX – size, midY - size, midX + size, midY + size, Color.Red); ScreenItems.Add(nextElement);

Now that the collection of screen elements has been created, the Paint() function will be able to repaint the screen’s contents any time that any portion of the screen has been covered, as well as whenever the screen has been re-sized.

8-12 Introduction to Computer Programming Using C#

Project Assignments

Project 8.1: The Computerized Etch-A-Sketch

Some 50 years ago, one of the hottest Christmas toys was the Etch-A-Sketch; they’re still around today, in a variety of forms, and the basic concept hasn’t changed all that much. The toy consists of a plastic box with a “screen” covering most of the top. Just below the screen is a pair of knobs. When the left knob is turned, a horizontal line is drawn on the screen, while turning the right knob draws a vertical line. If you flip the entire box upside down and shake it, your artwork will be erased. With enough practice, it’s possible to generate some moderately sophisticated drawings on the screen.

For this project, you’ll implement a computerized version of the Etch-A-Sketch that uses mouse movement to generate the image. We’ll also jazz things up a bit by allowing a variety of colors and line widths in the drawings.

Step 1: Design the Basic Form

As a first step, set the form's WindowState attribute to Maximized and its Color attribute to White. Then, lay out the remainder of the form so it looks approximately as follows:

The form contains a Panel component on which all other components are to be placed. The Dock attribute of this panel is set to Top , which will “lock” the panel in place along the top of the form.

Once you’ve positioned the panel on the form, add a Label control with the caption Line Width , followed by a NumericUpDown control. Set the Minimum and Maximum attributes of the NumericUpDown component to 1 and 5, respectively. The net effect of these settings will be to allow the up and down arrows attached to the NumericUpDown component to step the number displayed in the Text Box control up and down, taking on any of the values 1 through 5.

Basic Graphics and Mouse Management Concepts 8-

The purpose of the three buttons on the panel should be self-evident. You could go ahead and implement the event handler for the Quit button, since this a fairly trivial task. Also, add a ColorDialog component to the form to support color selection.

Step 2: Defining the Necessary Support Variables

The program’s behavior is really fairly simple. If one of the mouse’s buttons is pressed, then whenever the mouse is moved across the form, the program will draw a “line” in the currently set color, using the currently defined pen width. Clicking the Clear button will erase the screen, while clicking the Quit button will terminate the program.

To keep track of the mouse button status, add a variable named drawing of type bool ; set the initial value of this variable to false. Whenever a “mouse down” event occurs, this variable will be set to true , while the corresponding “mouse up” events should reset the value to false. As mouse movement is detected, the value of this variable will be checked to determine whether or not a line should be drawn in response to that mouse movement.

Since drawing a line requires you to know both the start and end points of the line, you’ll also need to create a pair of int variables to hold the previous x and y mouse coordinates. These variables should be initially set to 0.

You’ll also need a variable of type Color to hold the current pen color setting; this variable should be given the initial value of Color.Black.

Finally, to support repainting, you’ll need to add the declaration:

using System.Collections;

to the start of your program, and define a variable of type ArrayList to hold all of the lines as they are drawn on the screen, perhaps like this:

ArrayList screenLines = new ArrayList();

Step 3: Define a Line Class to Support Repainting

In order to support repainting of the lines on the screen whenever any portion of the screen is obscured, you’ll need to define a “line” class that will track the location, color, and width of any line that is drawn. This class should be placed near the end of your program, just ahead of the final left brace. The first portion of the class definition should contain these entries:

class Line { int x1, y1, x2, y2; int width; Color penColor;

8-14 Introduction to Computer Programming Using C#

The purpose of each of these components should be self-explanatory.

The next piece of the class definition is the constructor, which is responsible for creating and initializing a variable of type line. It’s structure is fairly simple:

public Line(int x1, int y1, int x2, int y2, int width, Color penColor) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; this.width = width; this.penColor = penColor; }

Notice how the keyword this has been appended to several of the variable references. The purpose is to specify that the variable being assigned a value is the variable that is declared as part of the form, rather than the variable in the parameter list.

The only remaining element of the class is the draw() function, which, as the name implies, is responsible for actually drawing a line on the screen. It’s definition follows:

public void draw(Graphics g) { Pen p; using (p = new Pen(penColor, (int)(width))) { g.DrawLine(p, x1, y1, x2, y2); } }

Since this is the end of the class definition, you’ll also need to add one more closing brace following this function’s declaration.

Step 4: Defining the MouseDown and MouseUp Event Handlers

When a mouse button is pressed, it indicates that your program is about to begin drawing. This is also the best time to determine what the current line width and color should be. To support this, create an MouseDown event handler for the main form. Be sure you create these handlers for the form, and not for the panel or any other component on the form!

Within the MouseDown event handler, add instructions to save the current mouse position in the “previous x and y coordinate” variables that you defined earlier, and set the value of the variable drawing to true.

Basic Graphics and Mouse Management Concepts 8-

When the mouse button is released, your program should quit drawing the current line segment. Therefore, the only instruction you need in the MouseUp event handler is:

drawing = false;

Step 5: Define the MouseMove Event Handler

Whenever a “mouse movement” event occurs, your program should check to see if a mouse button is currently down. If it is, you’ll need to create a new “line” variable, using the current line color and line width settings, save that variable in the ListArray variable named screenLines that you created earlier, and draw the line on the screen. Finally, you’ll need to save the endpoint of the line so it can be the starting point of the next line segment.

First, declare a variable g , of type Graphics , and a variable l , of type Line. Then, if the variable drawing is currently true , do the following:

  • Initialize the variable l to hold the settings for the next line. If the variables prevX and prevY hold the previous mouse position, and penColor holds the current color setting for the pen, you could do this as follows:

l = new Line(prevX, prevY, e.X, e.Y, (int)(numericUpDown1.Value), penColor);

  • Add the new line to the set of screen lines so far, and draw the line on the screen:

allLines.Add(l); using (g = this.CreateGraphics()) l.draw(g);

  • Save the current mouse location for the next line segment:

prevX = e.X; prevY = e.Y;

Step 6: Implement the Color Button’s Event Handler

When the Color button is clicked, your program should display the ColorDialog :

ColorDialog1.ShowDialog();

After control returns from this dialog box, your program should save the value of the color that was selected from the dialog box in the “saved pen color” variable. Recall that this value can be

8-16 Introduction to Computer Programming Using C#

found by referencing ColorDialog1.Color;

Step 7: Implement the Clear Button’s Event Handler

When the keyboard user clicks on the New entry in the menu, the drawing area must be erased. This task can be easily carried out like this:

Graphics g;

using (g = this.CreateGraphics()) g.Clear(this.BackColor);

In addition, you need to discard all of the save shapes from the previous drawing; otherwise, they’ll suddenly reappear if a screen repaint is required. To do this, use the instruction:

screenLines.Clear();

Finally, restore the current pen to its initial default setting of Color.Black for the pen, and reset the value in the NumericUpDown control to 1.

Step 8: Add Support for Repaint Requests

Presumably, your program can now draw “lines” on the screen in various colors and widths. However, whenever the ColorDialog opens, or any other screen-obscuring events occur, a part of your drawing will be erased from the screen. To ensure that the screen is restored whenever any portion of it is obscured, you should create a Paint event handler for your main form, and add instructions to redraw the screen by stepping through the ArrayList of screen elements, and redrawing each element on the screen:

using (Graphics g = this.CreateGraphics()) foreach (Line line in allLines) { line.draw(g); }

Step 9: Try Your Hand at Creating Some Masterpieces

This should be self-explanatory

Basic Graphics and Mouse Management Concepts 8-

Project 8.2: Simple Sketching

For this project, you'll be developing a simple drawing program that uses the mouse to sketch geometric shapes on the screen. The program will support lines, rectangles, and ellipses, and will allow for changing of the colors of these shapes.

Step 1: Design the Basic Form

As a first step, set your form's WindowState attribute to Maximized and its BackColor attribute to White. Next, add a MenuStrip component with 4 main menu entries: New , Color , Shape , and Quit. Under the Color entry, there should be sub-menu entries for Brush and Pen , while the Shape entry should have sub-menu options for Line , Rectangle , and Ellipse. Finally, add a ColorDialog component to the form. Also, change the caption at the top of the form to read Paint Shop Amateur.

An Overview of the Program's Processing

Since this program is expected to perform simple “paint” operations, you probably have a good idea of its basic behavior already. Selecting New will clear the window. Choosing either the Pen or Brush options from the Color menu entry will allow the user to select an outline or fill color. Selecting one of the shapes from the Shape sub-menu will define the current drawing shape, and choosing Quit will make the program terminate.

To actually draw something on the screen, the keyboard user would first select the shape and color attributes, and then positions the shape on the screen with the mouse. Pressing down the mouse button will define the object’s starting position, while releasing the button will define the ending position. For a line, these two points are then connected. For an ellipse or rectangle, however, the shape is drawn inside the rectangle whose opposite corners are defined by these points.

Step 2: Define a Shape Class to Support Repainting

As you should be expecting, you’ll want to define a “shape” class that can be used to describe the individual components as they are drawn. This class should be placed near the end of your program, just ahead of the final left brace. The first portion of the class definition should contain these entries:

class Shape { public enum Shapes { LINE, RECTANGLE, ELLIPSE }; int x1, y1, x2, y2; Color penColor, brushColor; Shapes shape;

8-18 Introduction to Computer Programming Using C#

The enumerated item, Shapes , is used to differentiate between the 3 supported shape types, while the remaining variables define the screen location of the shape, the coloring information for the shape, and the actual identification of the type of shape.

The next portion of the class will contain he constructor for a new variable of type Shape :

public Shape(Shapes shape, int x1, int y1, int x2, int y2, Color penColor, Color brushColor) { this.shape = shape; this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; this.penColor = penColor; this.brushColor = brushColor; }

As you can see, all that the constructor does is record the information that is passed to it.

The final portion of the class is the Draw function. For ellipses and rectangles, the function first draws the interior of the shape, and then draws the outline. For lines, the function uses the DrawLine function to connect the endpoints of the line. The definition of this function follows:

public void draw(Graphics g) { Brush b; Pen p;

using (b = new SolidBrush(brushColor)) using (p = new Pen(penColor)) { if (shape == Shapes.ELLIPSE) { g.FillEllipse(b, x1, y1, x2, y2); g.DrawEllipse(p, x1, y1, x2, y2); } else if (shape == Shapes.RECTANGLE) { g.FillRectangle(b, x1, y1, x2, y2); g.DrawRectangle(p, x1, y1, x2, y2); } else // The only choice left is a line { g.DrawLine(p, x1, y1, x2, y2); } } }

Basic Graphics and Mouse Management Concepts 8-

Since this is the end of the class definition, you’ll also need to add one more closing brace following this function’s declaration.

Step 3: Define the Necessary Support Variables

In order to keep track of everything that’s going on within this program, you’ll need to define several form variables. Two of these, of type Color , will be used to keep track of the current pen and brush settings, and should be initialized to Color.Black and Color.Transparent , respectively. You’ll also need a variable of type Shape.Shapes that will be used to keep track of the currently selected shape; I’d suggest naming this variable currShape. This variable should be initialized to Shape.Shapes.LINE.

In order to keep track of all of the shapes that are created by your program, you’ll need to define and initialize a variable of type ArrayList. Recall that this will require you to first add the directive using System.Collections; at the start of your program. The declaration and initialization of this variable might have the form:

ArrayList screenShapes = new ArrayList();

Finally, you’ll need to define two variables of type int that can be used to keep track of the mouse’s location when the mouse button is first depressed. These variables should both be initialized to 0.

Step 4: Define the Mouse Event Handlers

As specified earlier, the program will draw objects in response to MouseDown and MouseUp events. When a mouse button is pressed, your program needs to save the current position of the mouse, as defined by the event handler’s e.X and e.Y parameters, in the variables that you just declared for this purpose.

When the mouse button is released, the program needs to create a new Shape variable, corresponding to the currently selected shape, the current pen and brush color settings, the mouse’s position when the mouse button was initially pressed, and the mouse’s position when the button was subsequently released. First, you’ll need to declare a few variables:

Shape newShape; int x1, y1, x2, y2; Graphics g;

If the currently selected shape is a line, you need to copy the previous x and y mouse coordinates to the variables x1 and y1 , then copy the current x and y mouse coordinates, e.X and e.Y , to the variables x2 and y.

8-20 Introduction to Computer Programming Using C#

If, on the other hand, the shape is either a rectangle or an ellipse, you need to set the variable x to the smaller of the previous and current x mouse coordinates, and set x2 to the difference between the previous and current x mouse coordinates, perhaps like this:

if (prevX < e.x) { x1 = prevX; x2 = e.X – prevX; } else { x1 = e.X; x2 = prevX – e.X; }

Of course, these instructions assume you’ve used the variable name prevX for the previous (saved) x mouse coordinate. A similar instruction sequence will then be used to set y1 and y2 to the smaller of the two y mouse coordinates, and the difference between the two y mouse coordinates, respectively.

Once these parameters have been defined, you can “create” the new shape variable, draw the shape on the screen, and add the shape variable to the collection of all screen elements:

S = new Shape(currShape, x1, y1, x2, y2, penColor, brushColor); using (g = this.CreateGraphics()) S.draw(g); screenShapes.Add(S);

This code assumes that you used the name currShape for the variable that you defined earlier to hold the currently selected shape, and the names penColor and brushColor for the variables holding the current pen and brush color settings. If you used different names, you’ll have to make the appropriate adjustments.

At this point, you should be able to test the basic paint program. Even though the New , Shape , and Color options aren’t implemented, you should be able to use the mouse to draw black lines, anyway.

Step 5: Implement the Quit Option

This shouldn’t really need any explanation by now. Simply double-click the menu’s Quit entry, and place a Close() instruction in the corresponding event handler.

Step 6: Implement the Menu’s New Event

When the keyboard user clicks on the New entry in the menu, the drawing area must be erased. This task can be easily carried out like this:

Basic Graphics and Mouse Management Concepts 8-

Graphics g;

using (g = this.CreateGraphics()) g.Clear(this.BackColor);

In addition, you need to discard all of the save shapes from the previous drawing; otherwise, they’ll suddenly reappear if a screen repaint is required. To do this, use the instruction:

screenShapes.Clear();

Finally, restore the current pen and brush colors, along with the current shape, to their initial default settings - Color.Black for the pen, Color.Transparent for the brush, and Shape.Shapes.Line for the current shape.

Step 7: Implement the Remaining Menu Actions

When the keyboard user selects either the pen or brush color options from the main menu, your program should display the color dialog; if it still has its initial default setting, you’d use the instruction ColorDialog1.ShowDialog(). Once the color dialog box has been closed, you can then copy the selected color setting to the appropriate pen or brush color variable.

When a shape selection entry is selected, all you need to do is set the “current shape” variable to match the selected shape. For example, if the keyboard user clicks on the ellipse entry, you could save that selection with the instruction:

currShape = Shape.Shapes.ELLIPSE;

At this point, you should again test your program and make sure everything is working as expected.

Step 8: Add Support for Repaint Requests

Presumably, your program, at this point, can successfully draw all of the required shapes, using any color choices you wish. However, whenever the ColorDialog opens, or any of the menu pull-downs are opened, portions of your drawing will be erased from the screen. To ensure that the screen is restored whenever any portion of it is obscured, you should create a Paint event handler for your main form, and add instructions to redraw the screen by stepping through the ArrayList of screen elements, and redrawing each element on the screen:

using (Graphics g = this.CreateGraphics()) foreach (Shape s in screenShapes) { s.draw(g); }

8-22 Introduction to Computer Programming Using C#

If you’ve got some spare time, see if you can identify any quirky behavior in the program, and try to figure out if there’s some way to correct it. For example, what happens if you begin to select a pen or brush color, but don’t click the OK button before going back to draw some more? Also, can you press down the left mouse button, then the right mouse button, and then release only one of the buttons? What happens when you then release the other button? Can you change the current shape while the mouse button is pressed down?