Now you're ready to write code to draw inside the view window. You'll
be making a few changes directly to the EX03A source code.
The OnDraw Member Function
Specifically, you'll be fleshing out OnDraw in ex03aView.cpp. OnDraw is a virtual member function of the CView class that the application framework calls every time the view window needs to be repainted. A window needs to be repainted if the user resizes the window or reveals a previously hidden part of the window, or if the application changes the window's data. If the user resizes the window or reveals a hidden area, the application framework calls OnDraw, but if a function in your program changes the data, it must inform Windows of the change by calling the view's inherited Invalidate (or InvalidateRect) member function. This call to Invalidate triggers a later call to OnDraw.
Even though you can draw inside a window at any time, it's
recommended that you let window changes accumulate and then process them all together
in the OnDraw function. That way your program can respond both to
program-generated events and to Windows-generated events such as size changes.
The Windows Device Context
Recall from Chapter 1 that Windows doesn't allow direct access to the display hardware but communicates through an abstraction called a "device
context" that is associated with the window. In the MFC library, the device context is
a C++ object of class CDC that is passed (by pointer) as a parameter to
OnDraw. After you have the device context pointer, you can call the many
CDC member functions that do the work of drawing.
Adding Draw Code to the EX03A Program
Now let's write the code to draw some text and a circle inside the view window. Be sure that the project EX03A is open in Visual C++. You can use the Workspace window's ClassView to locate the code for the function (double-click on OnDraw), or you can open the source code file ex03aView.cpp from FileView and locate the function yourself.
void CEx03aView::OnDraw(CDC* pDC) { CEx03aDoc* pDoc = GetDocument(); ASSERT_VALID(pDoc); // TODO: add draw code for native data here }
The following boldface code (which you type in) replaces the previous code:
void CEx03aView::OnDraw(CDC* pDC) { pDC->TextOut(0, 0, "Hello, world!"); // prints in default font // & size, top left corner pDC->SelectStockObject(GRAY_BRUSH); // selects a brush for the // circle interior pDC->Ellipse(CRect(0, 20, 100, 120)); // draws a gray circle // 100 units in diameter }
You can safely remove the call to GetDocument because we're not dealing with documents yet. The functions TextOut, SelectStockObject, and Ellipse are all member functions of the application framework's device context class CDC. The Ellipse function draws a circle if the bounding rectangle's length is equal to its width.
The MFC library provides a handy utility class, CRect, for Windows rectangles. A temporary CRect object serves as the bounding rectangle argument for the ellipse drawing function. You'll see more of the CRect class in quite a few of the examples in this book.
For Win32 ProgrammersRest assured that the standard Windows WinMain and window procedure functions are hidden away inside the application framework. You'll see those functions later in this book, when the MFC library frame and application classes are examined. In the meantime, you're probably wondering what happened to the WM_PAINT message, aren't you? You would expect to do your window drawing in response to this Windows message, and you would expect to get your device context handle from a PAINTSTRUCT structure returned by the Windows BeginPaint function.
It so happens that the application framework has done all the dirty work for you and served up a device context (in object pointer form) in the virtual function OnDraw. As explained in Chapter 2, true virtual functions in window classes are an MFC library rarity. MFC library message map functions dispatched by the application framework handle most Windows messages. MFC version 1.0 programmers always defined an OnPaint message map function for their derived window classes. Beginning with version 2.5, however, OnPaint was mapped in the CView class, and that function made a polymorphic call to OnDraw. Why? Because OnDraw needs to support the printer as well as the display. Both OnPaint and OnPrint call OnDraw, thus enabling the same drawing code to accommodate both the printer and the display.