The GDI+Painter Application
Almost every chapter of this book will show a real-world example to illustrate the concepts discussed in it. In this chapter we create an application, GDI+Painter, that you can use to draw and fill simple graphics objects. If you wish, you can add more functionality to the application. Once you are done drawing graphics shapes, the program allows you to save your drawing in bitmap format. You can modify the program to save a drawing in .jpeg or .gif format.
The program is a Windows Forms application and looks like Figure 3.42. It has three draw buttons (line, ellipse, and rectangle) and two fill buttons (rectangle and ellipse). The Save Image button allows you to save the image.
Figure 3.42. The GDI+Painter application
Click on a button and the program draws the selected item on the form. Here's how it works:
First we define some private class-level variables:
// Variables private Bitmap bitmap = null; private Bitmap curBitmap = null; private bool dragMode = false; private int drawIndex = 1; private int curX, curY, x, y; private int diffX, diffY; private Graphics curGraphics; private Pen curPen; private SolidBrush curBrush; private Size fullSize;
Note
Please download GDI+Painter application source code from online (www.awprofessional.com/titles/0321160770).
The next step is to initialize objects. On the form-load event handler, we create a bitmap and a Graphics object from the bitmap, which represents the entire form. We set its background color to the form's background color by calling the Graphics.Clear method. We also create a Pen object and a Brush object when the form loads. Listing 3.31 gives the form-load event handler code.
Listing 3.31 The form-load event handler
private void Form1_Load(object sender, System.EventArgs e) { // Get the full size of the form fullSize = SystemInformation .PrimaryMonitorMaximizedWindowSize; // Create a bitmap using full size bitmap = new Bitmap(fullSize.Width, fullSize.Height); // Create a Graphics object from Bitmap curGraphics = Graphics.FromImage(bitmap); // Set background color as form's color curGraphics.Clear(this.BackColor); // Create a new pen and brush as // default pen and brush curPen = new Pen(Color.Black); curBrush = new SolidBrush(Color.Black); }
When we click on a button, we find out which button was selected and save it in the drawIndex variable. Listing 3.32 gives code for the button click event handler for all buttons.
Listing 3.32 Saving a selected button
private void LineDraw_Click(object sender, System.EventArgs e) { drawIndex = 1; } private void RectDraw_Click(object sender, System.EventArgs e) { drawIndex = 2; } private void EllipseDraw_Click(object sender, System.EventArgs e) { drawIndex = 3; } private void FilledEllipse_Click(object sender, System.EventArgs e) { drawIndex = 5; }
When we start drawing on the form, we save the starting point on the mouse-down events and the ending point on the mouse-up events (see Listing 3.33). From these two points we can determine the area of the rectangle we're trying to draw. We use this rectangle in draw and fill methods.
On a mouse-move event, we calculate the difference between the ending and starting points that are used to draw the rectangle. Notice also that on mouse down we set dragMode to true, and on mouse up we set dragMode to false. On the basis of the area covered by user selection, we draw or fill objects on mouse up, which gives the user a visible drawing effect. You will also see the RefreshFormBackground method, which we will discuss shortly.
Listing 3.33 The mouse-down event handler
private void Form1_MouseDown(object sender, System.Windows.Forms.MouseEventArgs e) { // Store the starting point of // the rectangle and set the drag mode // to true curX = e.X; curY = e.Y; dragMode = true; } private void Form1_MouseMove(object sender, System.Windows.Forms.MouseEventArgs e) { // Find out the ending point of // the rectangle and calculate the // difference between starting and ending // points to find out the height and width // of the rectangle x = e.X; y = e.Y; diffX = e.X - curX; diffY = e.Y - curY; // If dragMode is true, call refresh // to force the window to repaint if (dragMode) { this.Refresh(); } } private void Form1_MouseUp(object sender, System.Windows.Forms.MouseEventArgs e) { diffX = x - curX; diffY = y - curY; switch (drawIndex) { case 1: { // Draw a line curGraphics.DrawLine(curPen, curX, curY, x, y); break; } case 2: { // Draw an ellipse curGraphics.DrawEllipse(curPen, curX, curY, diffX, diffY); break; } case 3: { // Draw a rectangle curGraphics.DrawRectangle(curPen, curX, curY, diffX, diffY); break; } case 4: { // Fill the rectangle curGraphics.FillRectangle(curBrush, curX, curY, diffX, diffY); break; } case 5: { // Fill the ellipse curGraphics.FillEllipse(curBrush, curX, curY, diffX, diffY); break; } } // Refresh RefreshFormBackground(); // Set drag mode to false dragMode = false; }
Now we add code to the form's paint event handler, which draws and fills the object. Listing 3.34 gives the code for the OnPaint method.
Listing 3.34 The OnPaint method
private void Form1_Paint(object sender, System.Windows.Forms.PaintEventArgs e) { Graphics g = e.Graphics; // If dragMode is true, draw the selected // graphics shape if (dragMode) { switch (drawIndex) { case 1: { g.DrawLine(curPen, curX, curY, x, y); break; } case 2: { g.DrawEllipse(curPen, curX, curY, diffX, diffY); break; } case 3: { g.DrawRectangle(curPen, curX, curY, diffX, diffY); break; } case 4: { g.FillRectangle(curBrush, curX, curY, diffX, diffY); break; } case 5: { g.FillEllipse(curBrush, curX, curY, diffX, diffY); break; } } } }
Here's a little trick. You may have noticed that we used the RefreshFormBackground method. This method sets the current drawing as the background of the form. Listing 3.35 gives code for the method.
Listing 3.35 The RefreshFormBackground method
private void RefreshFormBackground() { curBitmap = bitmap.Clone( new Rectangle(0, 0, this.Width, this.Height), bitmap.PixelFormat); this.BackgroundImage = curBitmap; }
The Save Image button allows us to save the image by simply calling the Save method of Bitmap. The Save method takes a file name and format. We use SaveFileDialog to select the file name. Listing 3.36 gives code for the Save Image button.
Listing 3.36 The Save Image button click handler
private void SaveBtn_Click(object sender, System.EventArgs e) { // Save file dialog SaveFileDialog saveFileDlg = new SaveFileDialog(); saveFileDlg.Filter = "Image files (*.bmp)|*.bmp|All files (*.*)|*.*" ; if(saveFileDlg.ShowDialog() == DialogResult.OK) { // Create bitmap and call Save method // to save it Bitmap tmpBitmap = bitmap.Clone (new Rectangle(0, 0, this.Width, this.Height), bitmap.PixelFormat); tmpBitmap.Save(saveFileDlg.FileName, ImageFormat.Bmp); } }
In the end we release all objects, which we can do on the form-closed event (see Listing 3.37).
Listing 3.37 The form-closed event handler
private void Form1_Closed(object sender, System.EventArgs e) { // Dispose of all public objects curPen.Dispose(); curBrush.Dispose(); curGraphics.Dispose(); }
In Chapter 4 we will add functionality to select different pens and brushes to draw and fill graphics shapes.