Painting the Background for a CFormView Derived Class

I wanted to paint bitmaps on forms, like Access does. So I wrote some code for that: First, add in your derived CFormView two members:

CPalette m_palette;
CBitmap m_bitmap;

Add the following functions to your derived class: OnDraw overrides the base class' function, and OnCtlColor is a handler for WM_CTLCOLOR message. Call in OnInitialUpdate the function:

GetBitmapAndPalette( IDB_BACK, m_bitmap, m_palette );

where IDB_BACK is the identifier for the bitmap you want as background.

The code uses Keith Rule's CMemDC class. I added it here, so you don't have to search for it.

Add to your view a handler for WM_ERASEBKGND message:

BOOL CMyView::OnEraseBkgnd(CDC* pDC)
{
   // TODO: Add your message handler code here and/or call default
   return FALSE;
}


void CMyFormView::OnDraw(CDC* pDC) 
{
   // TODO: Add your specialized code here and/or call the base class
   ::DrawTheBackground(this,pDC,&m_palette,&m_bitmap);
}

HBRUSH CMyFormView::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
{
   //replace with your own base class
   HBRUSH hbr = CDaoRecordView::OnCtlColor(pDC, pWnd, nCtlColor);

   // TODO: Change any attributes of the DC here
   if(nCtlColor==CTLCOLOR_STATIC){
      //this is only an example - you may have to modify here the
      //code in order to work properly for you
      if(pWnd->GetDlgCtrlID()==IDC_STATIC){
         pDC->SetBkMode(TRANSPARENT);
         //you can change the color of static text here
         //pDC->SetTextColor(RGB(255,255,255));
         return (HBRUSH)::GetStockObject(NULL_BRUSH);
      }
   }else pDC->SetBkMode(OPAQUE);   
   
   // TODO: Return a different brush if the default is not desired
   return hbr;
}



BOOL CMyFormView::GetBitmapAndPalette(UINT nIDResource,
                                      CBitmap &bitmap,
                                      CPalette &pal)
{
   LPCTSTR lpszResourceName = (LPCTSTR)nIDResource;
   HBITMAP hBmp = (HBITMAP)::LoadImage( AfxGetInstanceHandle(),
      lpszResourceName, IMAGE_BITMAP, 0,0, LR_CREATEDIBSECTION );
   if( hBmp == NULL )return FALSE;
   bitmap.Attach( hBmp );
   // Create a logical palette for the bitmap
   DIBSECTION ds;
   BITMAPINFOHEADER &bmInfo = ds.dsBmih;
   bitmap.GetObject( sizeof(ds), &ds );
   int nColors = bmInfo.biClrUsed ? bmInfo.biClrUsed : 1
      << bmInfo.biBitCount;
   // Create a halftone palette if colors > 256. 
   CClientDC dc(NULL);         // Desktop DC
   if( nColors > 256 )   pal.CreateHalftonePalette( &dc );
   else{
      // Create the palette
      RGBQUAD *pRGB = new RGBQUAD[nColors];
      CDC memDC;
      memDC.CreateCompatibleDC(&dc);
      memDC.SelectObject( &bitmap );
      ::GetDIBColorTable( memDC, 0, nColors, pRGB );
      UINT nSize = sizeof(LOGPALETTE) + (sizeof(PALETTEENTRY)
                                                * nColors);
      LOGPALETTE *pLP = (LOGPALETTE *) new BYTE[nSize];
      pLP->palVersion = 0x300;
      pLP->palNumEntries = nColors;
      for( int i=0; i < nColors; i++){
         pLP->palPalEntry[i].peRed = pRGB[i].rgbRed;
         pLP->palPalEntry[i].peGreen = pRGB[i].rgbGreen;
         pLP->palPalEntry[i].peBlue = pRGB[i].rgbBlue;
         pLP->palPalEntry[i].peFlags = 0;
      }
      pal.CreatePalette( pLP );
      delete[] pLP;
      delete[] pRGB;
   }
   return TRUE;
}


void DrawTheBackground(CDaoRecordView *view,
                       CDC *pDC,
                       CPalette *mp_palette,
                       CBitmap *mp_bitmap)
{
   if(pDC->IsPrinting())return;
   CRect rect;
   CPalette *old_palette=NULL;
   // Select and realize the palette
   if( pDC->GetDeviceCaps(RASTERCAPS) & RC_PALETTE &&
       mp_palette->m_hObject != NULL ){
      old_palette=pDC->SelectPalette( mp_palette, FALSE );
      pDC->RealizePalette();
   }
   view->GetClientRect(rect);
   pDC->DPtoLP(rect);
   CMemDC DC(pDC,rect);
   CDC dcImage;
   if (!dcImage.CreateCompatibleDC(pDC))return;
   BITMAP bm;
   mp_bitmap->GetBitmap(&bm);
   // Paint the image.
   CBitmap* pOldBitmap = dcImage.SelectObject(mp_bitmap);
   for(int i=((int)floor((double)rect.left/bm.bmWidth))*bm.bmWidth;
       i<=rect.right/*rect.Width()*/;i+=bm.bmWidth)
        for(int j=((int)floor((double)rect.top/bm.bmHeight))
            *bm.bmHeight;j<=rect.bottom/*rect.Height()
            */;j+=bm.bmHeight)
      DC->BitBlt(i, j, bm.bmWidth, bm.bmHeight, &dcImage,
                    0, 0, SRCCOPY);
   dcImage.SelectObject(pOldBitmap);
   pDC->SelectPalette(old_palette,FALSE);
   pDC->RealizePalette();
}


#ifndef _MEMDC_H_
#define _MEMDC_H_
//////////////////////////////////////////////////
// CMemDC - memory DC
//
// Author: Keith Rule
// Email: keithr@europa.com
// Copyright 1996-1997, Keith Rule
//
// You may freely use or modify this code provided this
// Copyright is included in all derived versions.
//
// History - 10/3/97 Fixed scrolling bug.
//                   Added print support.
//   
// This class implements a memory Device Context


class CMemDC : public CDC {
private:
   CBitmap m_bitmap;        // Offscreen bitmap
   CBitmap* m_oldBitmap;    // bitmap originally found in CMemDC
   CDC* m_pDC;              // Saves CDC passed in constructor
   CRect m_rect;            // Rectangle of drawing area.
   BOOL m_bMemDC;           // TRUE if CDC really is a Memory DC.
public:
   CMemDC(CDC* pDC, CRect &rect) : CDC(), m_oldBitmap(NULL),
         m_pDC(pDC)
   {
      ASSERT(m_pDC != NULL);    // If you asserted here, you passed
                                // in a NULL CDC.
      m_bMemDC = !pDC->IsPrinting();
      m_rect=rect;
      if (m_bMemDC){
         // Create a Memory DC
         CreateCompatibleDC(pDC);
         pDC->GetClipBox(&m_rect);
         m_bitmap.CreateCompatibleBitmap(pDC, m_rect.Width(),
                                         m_rect.Height());
         m_oldBitmap = SelectObject(&m_bitmap);
         SetWindowOrg(m_rect.left, m_rect.top);
      } else {
         // Make a copy of the relevent parts of the current DC
         // for printing
         m_bPrinting = pDC->m_bPrinting;
         m_hDC = pDC->m_hDC;
         m_hAttribDC = pDC->m_hAttribDC;
      }
   }

   
   ~CMemDC()
   {
      if (m_bMemDC) {
         // Copy the offscreen bitmap onto the screen.
         m_pDC->BitBlt(m_rect.left, m_rect.top, m_rect.Width(),
                          m_rect.Height(),
            this, m_rect.left, m_rect.top, SRCCOPY);
         //Swap back the original bitmap.
         SelectObject(m_oldBitmap);
      } else {
         // All we need to do is replace the DC with an illegal
         // value, this keeps us from accidently deleting the
         // handles associated with the CDC that was passed to
         // the constructor.
         m_hDC = m_hAttribDC = NULL;
      }
   }
   // Allow usage as a pointer
   CMemDC* operator->() {return this;}
   // Allow usage as a pointer
   operator CMemDC*() {return this;}
};


#endif