Exercise 25.1: What happens if you don't clear the cMemoryDC ?
Comment out the line _pMemDC->clear( clearrect ); from the cGraphicsMFC: :clear(const CRect &clearrect) code in graphicsmfc.h and see what happens. Drag a corner of the window to resize it. Can you figure out what's going on?
Exercise 25.2: Invalidate and flicker
Change the CPopView:: OnEraseBkgnd method to call the default CView method. Look at the flicker.
Exercise 25.3: Writing your own memory CDC code
Occasionally students rebel at using the cMemoryDC class. 'I want to use my own code; if I have to do this at our job, I want to be able to do it myself .' But do keep in mind that the MemoryDC.* files (and all the other software in this book) are well- tested public domain freeware that you are explicitly authorized to reuse in any way, shape or form, with no acknowledgement necessary. But even so, there are individualistic souls who want to be sure they can do it unaided. And who can blame them? There is a sense, after all, in which you never completely understand code unless you have written it yourself, and then corrected your inevitable errors in the compiler, and then maybe even stepped through the code in the debugger. So this exercise asks you to write a screen-persistent version of Pop in which you don't use our cMemoryDC class. Here's how.
Put a static CDC _memCDC variable into CPopView followed by a CBitmap _memBitmap and a couple of int _memcx, _memcy . We can live without the CBrush variable. Imitate the cMemoryDC constructor code inside the CPopView constructor. The initialization is the trickiest thing about a cMemoryDC , so this will be the hardest part of your work. Do it like this after using the GetSystemMetrics to set _memcx and _memcy to match the screen measurements.
CDC cDC_display; CBitmap pBitmap_old; cDC_display.CreateDC("DISPLAY", NULL, NULL, NULL); _memDC.CreateCompatibleDC(&cDC_display); _memBitmap.CreateCompatibleBitmap(&cDC_display, _memcx, _memcy)); pBitmap_old = _memDC.SelectObject(&_memBitmap); pBitmap_old->DeleteObject();
Copy this very exactly; resist the temptation to simplify things by leaving out the seemingly extra steps involving the temporary cDC_display variable. These steps are necessary. Now go to CPopView :: OnDraw . Imitate the cMemoryDC clear , using PatBlt with the WHITENESS as the last argument. And use BitBlt to imitate the copyTo code.
You don't need to change the CPopView destructor, because the destructor will automatically call destructors on the CPopView data fields in the order they appear, which means _memDC.DeleteDC() will happen, and then _memBitmap.DeleteObject() . Of course if you'd created similar objects on a temporary basis somewhere, you might want to kill them off yourself, just to be sure that the CDC dies before the CBitmap (because if the CBitmap were to die first the CDC would have a cranky ant-fit!).
And don't forget to override CView::OnEraseBkgnd(CDC *pDC) to do nothing at all but return TRUE; . If you forget this, your screen can still flicker, as the default behavior of any call to Invalidate() is to first call OnEraseBkgnd , and that function's default behavior is to erase the view with the background brush.