Introduction
This is actually a very simple task. The hard thing to figure out is the fact that the background part of the main frame Window isn't really handled by the frame Window itself. It contains another Window that fills the area inside the frames border. This Window is known as m_hWndMDIClient
.
Background
To get this task done, my first instinct was to catch the WM_ERASEBKGND
in CMainFrame
and do the FillSolidRect
there. But I soon realized that approach didn't do anything other than make the frame flicker and show red only when it's being resized, otherwise the gray background was completely intact. Not exactly what I was looking for. Suddenly it became a little mystery. Why doesn't the background of the frame change when I fill it with a color in the frame's WM_ERASEBKGND
handler? So I went on a hunt, I went through a lot of the CMDIFrameWnd
and CFrameWnd
code looking for methods that would draw on the frame, and there was not much there. I finally ended up at CMainFrame::OnCreate
method, I set a breakpoint and stepped through the CMDIFrameWnd::OnCreate
method. After what seemed to be hours of searching I came across the following code in CMDIFrameWnd::CreateClient
:
if ((m_hWndMDIClient = ::CreateWindowEx(dwExStyle, _T("mdiclient"), NULL,
dwStyle, 0, 0, 0, 0, m_hWnd, (HMENU)AFX_IDW_PANE_FIRST,
AfxGetInstanceHandle(), (LPVOID)&ccs)) == NULL)
{
TRACE(traceAppMsg, 0, _T("Warning: CMDIFrameWnd::OnCreateClient:
failed to create MDICLIENT.")
_T(" GetLastError returns 0x%8.8X\n"), ::GetLastError());
return FALSE;
}
It is pretty clear that the frame Window is creating another Window to act as its center (To this day I don't really know the purpose behind this). So that's where this code comes in. By subclassing the client Window m_hWndMDIClient
I was able to cleanly change the color of the MDI frame Window.
Using the code
To change the color of the background, first we must create a new CWnd
derived class which handles the WM_ERASEBKGND
message and fills the client area of the Window with a different color. So let's add a class called CClientWnd
which inherits from CWnd
:
#pragma once
class CClientWnd : public CWnd
{
DECLARE_DYNAMIC(CClientWnd)
public:
CClientWnd();
virtual ~CClientWnd();
protected:
afx_msg BOOL OnEraseBkgnd(CDC* pDC);
DECLARE_MESSAGE_MAP()
};
And implement the OnEraseBkgnd
method:
#include "stdafx.h"
#include "ClientWnd.h"
IMPLEMENT_DYNAMIC(CClientWnd, CWnd)
CClientWnd::CClientWnd()
{
}
CClientWnd::~CClientWnd()
{
}
BEGIN_MESSAGE_MAP(CClientWnd, CWnd)
ON_WM_ERASEBKGND()
END_MESSAGE_MAP()
BOOL CClientWnd::OnEraseBkgnd(CDC* pDC)
{
CRect Rect;
GetClientRect(&Rect);
pDC->FillSolidRect(&Rect,RGB(255,0,0));
return TRUE;
}
Now the only thing left to do is to subclass the Window m_hWndMDIClient
. To do that we are going to overwrite CMainFrame::OnCreateClient
method, and declare m_Client
in CMainFrame
's class declaration as a private variable.
class CMainFrame : public CMDIFrameWnd
{
......
protected:
virtual BOOL OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext);
private:
CClientWnd m_Client;
};
Implementation of OnCreateClient
should look like this:
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT lpcs, CCreateContext* pContext)
{
if (CMDIFrameWnd::OnCreateClient(lpcs, pContext))
{
m_Client.SubclassWindow(m_hWndMDIClient);
return TRUE;
}
return FALSE;
}
Have fun!