Generally, it’s not necessary to change the dialog’s default background. Besides, if have to display an image in a dialog, it is preferable to do it in a child control (e.g. static picture or custom control). However, let’s say that’s a requirement or we want to do it as an exercise or just for fun.
The image shows a dialog that has its background painted with a fancy brush. It also has a nice Earth image, transparently painted directly in the dialog’s client area.
Let’s see how to make it!.
Handling WM_ERASEBKGND message
This example handles the WM_ERASEBKGND message and fills the background by using a custom brush. Finally, it returns TRUE to prevent further erasing.
BOOL CDemoDialog::OnEraseBkgnd(CDC* pDC) { // get clipping rectangle CRect rcClip; pDC->GetClipBox(rcClip); // fill rectangle using a given brush pDC->FillRect(rcClip, &m_brushBack); return TRUE; // returns non-zero to prevent further erasing }
Next’ we would notice that’s necessary to handle WM_CTLCOLOR in order to change also the beackground of some child controls (static, radio buttons, etc.). As long as, WM_CTLCOLOR allows also changing the dialog’s background, we can get rid of previous method and do all the work in the WM_CTLCOLOR message handler.
Handling WM_CTLCOLOR message
To change the default background as well as other attributes, WM_CTLCOLOR message handler (OnCtlColor) must return a not-NULL brush handle.
HBRUSH CDemoDialog::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hBrush = NULL; switch(nCtlColor) { case CTLCOLOR_DLG: // just return a not NULL brush handle hBrush = (HBRUSH)m_brushBack; break; case CTLCOLOR_STATIC: { // set text color, transparent back node then pDC->SetTextColor(m_crStaticText); pDC->SetBkMode(TRANSPARENT); // return a not NULL brush handle hBrush = (HBRUSH)m_brushBack; } break; default: // do the default processing hBrush = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); break; } return hBrush; }
Note: In Win32, WM_CTLCOLOR notification message was replaced by WM_CTLCOLORDLG, WM_CTLCOLORSTATIC, and so on. However, MFC still keeps WM_CTLCOLOR for 16-bit legacy code compatibility. The third argument (nCtlColor) of OnCtlColor handler function, helps us to decide if in fact has been received one of the following notifications: WM_CTLCOLORDLG, WM_CTLCOLORSTATIC, etc.
Handling WM_PAINT message
Finally, lets’s “paint the Earth”! As in any other kind of window, the right place to perform paining is the WM_PAINT message handler.
void CDemoDialog::OnPaint() { CPaintDC dc(this); // device context for painting CDC dcMem; // create memory DC dcMem.CreateCompatibleDC(&dc); // select the source bitmao into the memory DC CBitmap* pOldBitmap = (CBitmap*)dcMem.SelectObject(&m_bmpEarth); // get the bitmap data BITMAP bmp = {0}; m_bmpEarth.GetBitmap(&bmp); // transfer the bitmap into paint DC using a transparent color dc.TransparentBlt( 10, 10, bmp.bmWidth, bmp.bmHeight, // destination coordinates and sizes &dcMem, // source DC 0, 0, bmp.bmWidth, bmp.bmHeight, // source coordinates and sizes RGB(255, 0, 0)); // transparent color // restore DC / free GDI objects dcMem.SelectObject(pOldBitmap); }
Demo project
Download the demo project and you can find more implementation details.
- Demo project: Painting the Dialog Backround.zip (75)
Resources
- MSDN: CWnd::OnEraseBkgnd
- MSDN: CWnd::OnCtlColor
- MSDN: CWnd::OnPaint
See also
- Codexpert blog: Custom Paint in MDI Client