Custom Paint in MDI Client

In some MDI applications, would be nice to perform some custom painting the MDI Client area. For example we can fill the background with a brush or draw a logo image. One classic and well known method is to subclass the MDI Client window in our own class derived from CWnd MFC class. After subclassing, all we have to do is to handle WM_PAINT and/or WM_ERASEBKGND and perform our custom painting. There are tens of articles describing this method in detail, and you cand find some by clicking the links shown in the bottom of this article.

That works great if the main frame class is derived from CMDIFrameWnd, but it’s no more possible if the main frame class is derived from CMDIFrameWndEx (shipped with Visual Studio 2008 with Feature Pack and newer Visual Studio versions). That’s because CMDIFrameWndEx subclases the MDI Client window, itself (see CMDIFrameWndEx::m_wndClientArea of type CMDIClientAreaWnd). MFC, adds the subclassed window in a permanent map and does not allow subclassing twice. One can say “well, just set CMDIFrameWndEx::m_bDoSubclass to FALSE before calling CMDIFrameWndEx::OnCreateClient; next we can do subclassing as we wish”. That doesn’t work either, because in many places the framework expects using exactly CMDIFrameWndEx::m_wndClientArea and nothing else.

Searching for a solution

In an older discussion about a related subject, Joseph M. Newcomer proposed a simple trick: temporarily attach the MDI Client window handle to your own class, whenever it’s necessary to force  repainting the MDI Client area.

void CMyApp::RedrawClientArea()
CMySublcass w;

Next, just have to call CMyApp::RedrawClientArea in our MFC application, where necessary. That’s IMO very ingenious and should work but, as the author states himself, it’s “remarkably ugly”. So I spent some time  trying other tricks, which included the “hacking” of application thread maps. None was useful.

The right solution

Finally, I found the better solution. It is incredible simple: just have to override CMDIFrameWndEx::OnEraseMDIClientBackground virtual function!
Here is an example that fills the MDI Client area using a given brush.

BOOL CMainFrame::OnEraseMDIClientBackground(CDC* pDC)
   CRect rcClient;

   // paint MDI Client background 
   CBrush* pOldBrush = pDC->SelectObject(&m_brushMDIBack);
   pDC->PatBlt(0, 0, rcClient.right, rcClient.bottom, PATCOPY);

   // free GDI objects

   return TRUE; // no further default processing

Demo project

The demo project, attached here, is an MDI MFC application that paints in the MDI Client a custom background and a logo image.

Custom Paint in MDI Client - Demo
Custom Paint in MDI Client – Demo



See also


4 thoughts on “Custom Paint in MDI Client”

  1. The solution that you have presented here are elegant, but there are not possible in VC6, and here I mean on CMainFrame::OnEraseMDIClientBackground … I had tried with CMainFrame::OnEraseBackground, but did not function … there is a solution for VC6, or I should apply a temporary MDI client window ? Thank you.

  2. Worked Great! Thanks. Can we draw on the background with say shapes and text? I tried it and the background did not clear properly…

    • Yes, you can draw shapes and text, as well. Use double buffering, i.e. first draw into a memory DC and then copy the contents of memory DC into the window DC (using BitBlt).


Leave a Comment