What we have in this page?
Try running the program, minimize the window to the taskbar, and bring it back by clicking the taskbar button. You’ll find that all the graphics have disappeared, and you’ll also find that the same thing happens if you let another window display on top of the Drawing application window. You can regenerate the graphics by clicking the Draw button again, but why does the drawing disappear?
What’s happening here is quite normal. Windows takes care of refreshing the components on the form, the form itself, the title bar, and the button, but it’s your responsibility to refresh anything you have drawn on the client area of the form. Whenever a window needs to be refreshed, the operating system will send it a Paint event. If you handle these Paint events, you can ensure that the form will always display its data. The following exercise shows you how to handle Paint events and also how to interact with the mouse. The program will capture MouseDown and MouseUp events when you click and release the mouse button, and it will draw a line between the point where you clicked the button and the point where you release it.
8. Continue with the CppDraw application from the previous exercise. You can remove the Button and handler code because you won’t be using it in this exercise, but it will do no harm to leave it in.
9. Add two Point members to the Form1 class.
Point p1, p2;
Because Point is a value type, these members are declared as variables and not as pointers. The MouseDown event is fired when any mouse button is clicked down while the mouse is over a control; the MouseUp event is fired when the mouse button is released.
10. Arrange for handler functions to be called for the MouseDown and MouseUp events on the form by using the Properties editor to add handlers for the MouseUp and MouseDown events. Double click in the empty field of the MouseDown and MouseUp events.
11. Now edit the code for the MouseDown event handler so that it will save the position of the mouse pointer at the time the button was clicked.
private: System::Void Form1_MouseDown(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e)
p1.X = e->X;
p1.Y = e->Y;
The handler functions for mouse-related events get sent MouseEventArgs objects, which contain information about the event. MouseEventArgs has several useful properties:
Button tells you which mouse button was clicked.
Clicks tells you the number of mouse clicks that triggered the event.
Delta tells you the number of notches the wheel on a wheel- equipped mouse was rotated.
X and Y tell you the X and Y coordinates of the mouse when the event was fired.
In this example, you’re saving the X and Y values in the variable p1. Because p1 is a value type, you have to use dot notation to access its members.
12. The MouseUp handler is similar, in that it also stores the coordinates.
private: System::Void Form1_MouseUp(System::Object^ sender, System::Windows::Forms::MouseEventArgs^ e)
p2.X = e->X;
p2.Y = e->Y;
Graphics^ gr = CreateGraphics();
Pen^ pen1 = gcnew Pen(Color::Black);
gr->DrawLine(pen1, p1.X, p1.Y, p2.X, p2.Y);
Once the coordinates have been stored, the function draws a line between the two points.
13. If you build and run the application at this point, you should be able to draw lines on the form using the mouse. You’ll find, however, that they disappear whenever the form needs to be repainted, just as in the previous exercise. In the rest of this exercise, you’ll save coordinates of the lines as you draw them and then redraw the collection of lines whenever the form needs to be repainted.
14. You’re going to store the line information in an ArrayList, which is part of the System::Collections namespace. This namespace is already referenced in Windows Forms projects, so you don’t need to add a using directive to your code as shown below.
15. Add a private ArrayList member to the Form1 class.
16. Create the ArrayList object in the Form1 constructor, adding the line after the call to InitializeComponent.
list = gcnew ArrayList();
17. Add a new managed struct named Line to the top of the program, after the using directives and before the declaration of the Form1 class.
ref struct Line
This struct simply holds two Points that represent the endpoints of the line. You could easily extend this type to hold other information, such as color and line style. Every time you draw a line, create a new Line object and add it to the ArrayList. Unfortunately when you go to the designer, the following error occurred.
Following the instruction also no avail. So what the hell is this? Searching the Internet for this message "Either VCProject or VCCodeModel is not ready yet. Please close designer and try again." error we found this, this, this. Also happened in Visual Studio 2005.
18. Let change the location of the struct. When examining the ref class properties of the Form1, it is CppDraw::Form1 and in our case the CppDraw::Line is not working so we change the location so that we have CppDraw::Form1::Line.
19. Change the ref struct location just before the #pragma region. The Line struct properties is shown in the following figure. Well, no more designer error and if you still having the error, close and re-open the project solution.
20. Add the following code to the end of the Form1_MouseUp function, after the call to DrawLine and before the call to delete:
// Add a new line to the list
Line^ pline = gcnew Line();
pline->p1 = p1;
pline->p2 = p2;
The Line object simply holds the Points that represent the endpoints of the line.
21. Use the Properties editor to add a handler for Paint event. Go to the Events page and double click on the empty field on the right of the Paint event. Add the following code for the Paint event handler.
private: System::Void Form1_Paint(System::Object^ sender, System::Windows::Forms::PaintEventArgs^ e)
Graphics^ gr = e->Graphics;
Pen^ pen1 = gcnew Pen(Color::Black);
// Count member - Used to get the number of elements actually
// contained in the ArrayList.
for(int i=0; i<list->Count; i++)
// Use default for the Item ArrayList member
// Item's property of the ArrayList member - Used to get or set the
// element at the specified index.
// Then, cast to the Line^ type and assign to pline variable
Line^ pline = dynamic_cast<Line^>(list->default[i]);
gr->DrawLine(pen1, pline->p1.X, pline->p1.Y, pline->p2.X, pline->p2.Y);
The function first gets a Graphics object and then creates a Pen. When you’re handling a Paint event, you get a Graphics object as part of the PaintEventArgs argument; use this one, and don’t create your own. Likewise, don’t call Dispose/delete on this object when you finish with it; you didn’t create it, so it isn’t your responsibility to dispose of it. The code then loops over all the items in the ArrayList, retrieving each one and casting it from an Object^ (using the default get and set properties of the Item member) back to a Line^. Once this loop is finished, you can use the coordinates in the Line object to draw the lines.
22. Build and run the program now. You’ll find that the lines you draw persist through minimizing, maximizing, and other window events that cause a repaint. Draw something and then try minimizing and maximizing the form. The drawing is persistent.