Make GDI Calls the MFC Way

In your original Windows code, the device context appears as a handle to a GDI data structure (a DC). Certain Windows API functions — the GDI calls — operate on a device context when you pass them a handle to one. In contrast, MFC encapsulates the device-context handle (HDC) inside an MFC object of class CDC. Using the device context the MFC way requires a different syntax and a different mode of thinking.

The model just described is one example of a general model in MFC, which comes from the use of C++. MFC encapsulates common Windows-based entities — device contexts, windows, dialog boxes, GDI objects such as brushes — in MFC classes. To use these classes, you (a) create an object of the class (or MFC does) and (b) call member functions of the class to manipulate it.

Making GDI calls the MFC way occurs mainly in the view's OnDraw member function. Making other Windows API calls the MFC way occurs throughout the MFC portions of your program.

In Moving Your Paint Code to OnDraw, you saw SHOWDIB's WM_PAINT code as it appears in the OnDraw member function override. That code doesn't take full advantage of MFC, though, since it ignores the pointer to a CDC object that OnDraw receives as a parameter:


void CShowDibView::OnDraw(CDC* pDC)

That CDC object encapsulates a completely prepared device context, but instead of using it, the OnDraw code gets its own device context:


hDC = ::BeginPaint(hWnd, &ps);

That version of OnDraw also calls Windows API functions directly rather than calling the MFC equivalents, requiring use of the C++ scope resolution operator to disambiguate the calls.

A New OnDraw

Here's a version of OnDraw that (a) uses the CDC pointer and (b) calls MFC versions of the Windows API functions:


void CShowDibView::OnDraw(CDC* pDC) { HWND hWnd = GetSafeHwnd(); // for converting hWnd parameters HDC hDC = pDC->GetSafeHdc(); // for converting hdc parameters // TODO: add draw code for native data here /* If we have updated more than once, the rest of our * window is not in some level of degradation worse than * our redraw... we need to redraw the whole area */ if (UpdateCount > 1) { ValidateRect(NULL); // replaces Begin/EndPaint calls UpdateCount = 0; InvalidateRect(NULL); return; // replace break statement } // BeginPaint call deleted AppPaint(hWnd, hdc, GetScrollPos(SB_HORZ), GetScrollPos(SB_VERT) ); // EndPaint call deleted // delete break statement here }

In the second line of the function, this version of OnDraw calls GetSafeHdc (similar to GetSafeHwnd) through the pDC pointer. Recall that this syntax resembles accessing a struct's members through a pointer, but here the notation is used to access and call a member function of a C++ object instead. After this call, a safe HDC is available for use in the call to the SHOWDIB application-specific function AppPaint.

Instead of disambiguating the direct calls to the Windows API functions, as you saw earlier in WindowProc, this version of OnDraw uses the MFC versions of the same functions. Because the view class is ultimately derived from class CWnd, it inherits all of CWnd's member functions, including these. OnDraw, as a member of the view class, can simply call those inherited functions directly (without having to call through a pointer as with the HDC). Note that the MFC versions don't pass an HWND parameter and so are shorter and simpler.

You'll want to make such changes generally in your new MFC application, although you really only need to do so in the code associated with your derived MFC classes. Such changes aren't necessary in the original C code that you simply converted to C++.