您的位置:首页 > 其它

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

2017-02-13 13:45 267 查看
问题描述:



如上图所示,这是用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);

}

经过这样的修改,感觉上应该是可以了,每当我的子窗口触发重绘等操作时,就把客户区重新填充一下背景,但实际运行你会很失望,程序一创建时,客户区还是灰色的,当触发了重绘操作时,的确客户区变为我想要的绿色了,但每次拖动窗口,就会留下大片的阴影,效果不是一般的差。

有没有什么好的办法呢,其实上面的做法都是在客户区窗口自我绘制完后,又人为的在客户区窗口中重新绘制内容,这是治标不治本的做法,当客户区窗口每次更新时,都默认采用自己的灰色填充窗口背景,这是即时进行的,而我们添加的改变背景的代码则常常不能即时进行,所以,最根本的做法,就是在客户区窗口收到WM_PAINT这样的消息时,不再用默认的灰色填充窗口背景,而是改用我们希望的背景色或图片,写到这里,答案应该是呼之欲出了,那就是定制客户区窗口的默认窗口过程。于是,改动如下:

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct)

{

if (CMDIFrameWnd::OnCreate(lpCreateStruct) == -1)

return -1;

theApp.m_hMDIWnd=m_hWndMDIClient;

lpfnOldProc = (WNDPROC)SetWindowLong(m_hWndMDIClient,GWL_WNDPROC,(DWORD)SCWndProc);

……

}

SCWndProc是CMainFrame的静态成员函数:

LRESULT CALLBACK CMainFrame::SCWndProc(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)

{

RECT rc;

HBRUSH br;

HDC dc;

switch (wMsg) {

case WM_PAINT:

::GetClientRect(hWnd,&rc);

br=CreateSolidBrush(RGB(0,255,0));

dc=::GetDC(hWnd);

FillRect(dc,&rc,br);

break;

}

return CallWindowProc (lpfnOldProc, hWnd, wMsg, wParam, lParam);

}

别忘了程序关闭前,换回原来的窗口过程:

void CMainFrame::OnDestroy()

{

SetWindowLong(m_hWndMDIClient,GWL_WNDPROC,(DWORD)lpfnOldProc);

CMDIFrameWnd::OnDestroy();

}

这样基本可以了,但程序再一开始启动的时候,窗口背景还是灰色的,这是因为客户区才创建时,我们还没有为其指定新的窗口过程,补救的办法也简单,就是在CxxxxApp::InitInstance()的最后pMainFrame->UpdateWindow();代码之后,加上下面的代码:

RECT rc;

GetClientRect(m_hWndMDIClient,&rc);

HDC hdc=::GetDC(m_hWndMDIClient);

HBRUSH hbr=CreateSolidBrush(RGB(0,255,0));

::FillRect(hdc,&rc,hbr);

好了,基本上这样就差不多了,最后贴一张最终的效果图:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息