定制MFC多文档窗口的主框架背景

问题描述: 如上图所示,这是用MFC创建的多文档程序,可以看到主框架的背景区是灰色的,如果我要在这块区域贴一张图片,应该怎么办呢?最容易想到的是在CMainFrame的OnPaint中对背景进行更改,代码如下: (为了简便,这里改为设置窗口背景色,其实这和贴图的原理差不多) void CMainFrame::OnPaint() { CPaintDC dc(this); // device context for painting // TODO: 在此处添加消息处理程序代码 RECT rc; GetClientRect(&rc); dc.FillRect(&rc,&CBrush(RGB(0,255,0))); } 但运行之后你会发现并没有达到期望的效果。 下面我们来分析为什么会这样。


多文档窗口之主框架窗口的创建过程: 1. 在CMyTestApp::InitInstance中调用了 CMainFrame* pMainFrame = new CMainFrame; pMainFrame->LoadFrame(IDR_MAINFRAME)

2. 在CMDIFrameWnd::LoadFrame中调用了 CFrameWnd::LoadFrame(nIDResource, dwDefaultStyle,pParentWnd, pContext)

在CFrameWnd::LoadFrame中含有 Create( lpszClass, strTitle, dwDefaultStyle, rectDefault, pParentWnd, ATL_MAKEINTRESOURCE(nIDResource), 0L, pContext)

3. CFrameWnd::Create中调用了 CreateEx(dwExStyle, lpszClassName, lpszWindowName, dwStyle, rect.left, rect.top, rect.right - rect.left, rect.bottom - rect.top, pParentWnd->GetSafeHwnd(), hMenu, (LPVOID)pContext) 该函数的rect、pParentWnd、pContext等参数是没有值的,这会调用CWnd的CreateEx方法,创建的窗口句柄存在m_hWnd成员变量中 之后便进入到CFrameWnd::OnCreate,然后再返回到本函数中 该函数执行完后返回到第2步

4. CMainFrame::OnCreate 这是个消息响应函数,响应的是WM_CREATE消息,这里面调用了 CMDIFrameWnd::OnCreate(lpCreateStruct)

5. CFrameWnd::OnCreate调用了 OnCreateHelper(lpcs, pContext) lpcs中包含客户区、类名、标题、菜单等数据

6. CFrameWnd::OnCreateHelper调用了 CWnd::OnCreate(lpcs) 然后又调用了 OnCreateClient(lpcs, pContext)

7. CMDIFrameWnd::OnCreateClient中 CMenu* pMenu=GetMenu(); CreateClient(lpcs, pMenu)

CMDIFrameWnd中含有m_hWnd和m_hWndMDIClient成员变量,前者是在第3步时创建的CWnd窗口

8. CMDIFrameWnd::CreateClient中的部分代码 ASSERT(m_hWnd != NULL); ASSERT(m_hWndMDIClient == NULL); m_hWndMDIClient = ::AfxCtxCreateWindowEx(dwExStyle, _T(“mdiclient”), NULL, dwStyle, 0, 0, 0, 0, m_hWnd, (HMENU)AFX_IDW_PANE_FIRST, AfxGetInstanceHandle(), (LPVOID)&ccs)

9.返回到第3步


在上面的分析过程中,通过第8步,可以看到,在主窗口框架中又建立了一个子窗口,该窗口的类型为”mdiclient”,该窗口的句柄为m_hWndMDIClient。也就是说,程序在创建完主框架窗口后,又在该窗口内部创建了一个客户区窗口,该客户区窗口就是我们看到的图中灰色区域的部分,其实也是我们要改变背景的部分,如果没有这个客户区窗口,那我们前面改变主框架窗口背景的代码是有效果的,但当该窗口创建后(该窗口的背景色为灰色),就又把之前改变了背景色的区域给挡住了。

通过上面的分析,我们知道了,只要改变m_hWndMDIClient窗口的背景色就可以了,那具体该怎么做呢?

先说一种比较笨的办法,就是在CxxxxView的OnDraw、OnMoving、OnSizing等等消息响应函数中每次都对上面的客户区窗口设置背景色,具体做法为: 在CxxxxApp中先创建一个成员变量HWND m_hMdiClient(构造函数中初始化为NULL)。然后修改 int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) { if (CMDIFrameWnd::OnCreate(lpCreateStruct) == -1) return -1; theApp.m_hMdiClient=m_hWndMDIClient; //记录下客户区窗口句柄 …… } 最后在CxxxxView的OnDraw等方法中加上这样的代码: if(theApp.m_hMdiClient!=NULL) { RECT rc; GetClientRect(theApp.m_hMdiClient,&rc); HDC hdc=::GetDC(theApp.m_hMdiClient); HBRUSH hbr=CreateSolidBrush(RGB(0,255,0)); ::FillRect(hdc,&rc,hbr); } 经过这样的修改,感觉上应该是可以了,每当我的子窗口触发重绘等操作时,就把客户区重新填充一下背景,但实际运行你会很失望,程序一创建时,客户区还是灰色的,当触发了重绘操作时,的确客户区变为我想要的绿色了,但每次拖动窗口,就会留下大片的阴影,效果不是一般的差。

经验分享 程序员 微信小程序 职场和发展