Drawing Inside the View Window—The Windows Graphics Device Interface

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.

  1. Edit the OnDraw function in ex03aView.cpp. Find the AppWizard-generated OnDraw function in ex03aView.cpp:

    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.

  2. Recompile and test EX03A. Choose Build from the Project menu, and, if there are no compile errors, test the application again. Now you have a program that visibly does something!

For Win32 Programmers

Rest 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.