3.3 使用Windows的消息机制
使用MFC类的既有函数来美化界面,其功能是有限的。既然Windows是通过消息机制进行通讯的,那么我们就可以通过截获一些有用的消息来美化我们的界面,以下是一些有用的Windows消息:
WM_PAINT
WM_ERASEBKGND
WM_CTLCOLOR*
WM_DRAWITEM*
WM_MEASUREITEM*
NM_CUSTOMDRAW*
注意,标注*的消息是子元素发送给父窗口的通知消息,其它的为窗口或者子元素自身的消息。
3.3.1 WM_PAINT
WM_PAINT消息相信大家都很熟悉,一个窗口要重绘了,就会有一个WM_PAINT消息发送给窗口。
可以响应窗口的WM_PAINT,以更改它们的模样。WM_PAINT的映射函数原型如下:
afx_msg void OnPaint();
控件也是窗口,所以控件也有WM_PAINT消息,通过消息映射我们完全可以定义控件的界面。如图5所示:
图5 利用WM_ PAINT消息美化界面
实现代码也很简单:
[cpp] view plaincopy
- void CLazyStatic::OnPaint()
- {
- CPaintDC dc(this); // device context for painting
- //什么都不输出,仅仅画一个矩形框
- CRect rc;
- GetClientRect(&rc);
- dc.Rectangle(rc);
- }
[cpp] view plaincopy
- void CLazyStatic::OnPaint()
- {
- CPaintDC dc(this); // device context for painting
- //什么都不输出,仅仅画一个矩形框
- CRect rc;
- GetClientRect(&rc);
- dc.Rectangle(rc);
- }
哈哈,简单吧?不过WM_PAINT确实绝了点,它要求应用程序完成元素界面的所有绘制过程,想象一下如何画出一个完整的列表控件?太烦了吧。一般来说,很少有人喜欢使用WM_PAINT,还有其它更细致的消息。
3.3.2 WM_ERASEBKGND
Windows在向窗口发送WM_PAINT消息之前,总会发送一个WM_ERASEBKGND消息通知该窗口擦除背景,默认情况下,Windows将以窗口的背景色清除该窗口。
可以响应窗口(包括子元素)的WM_ERASEBKGND,以更改它们的背景。WM_ERASEBKGND的映射函数原型如下:
afx_msg BOOL OnEraseBkgnd( CDC* pDC );
返回值:
指定背景是否已清除,如果为FALSE,系统将自动清除
参数:
pDC指定了绘制操作所使用的设备环境。
图6是个简单的例子,通过OnEraseBkgnd为对话框加载了一副位图背景:
图6 利用WM_ ERASEBKGND消息美化界面
实现代码也很简单:
[cpp] view plaincopy
- BOOL CUi4Dlg::OnInitDialog()
- {
- //…
- //加载位图
- //CBitmap m_Back;
- m_Back.LoadBitmap(IDB_BACK);
- //…
- }
- BOOL CUi4Dlg::OnEraseBkgnd(CDC* pDC)
- {
- CDC dc;
- dc.CreateCompatibleDC(pDC);
- dc.SelectObject(&m_Back);
- //获取BITMAP对象
- BITMAP hb;
- m_Back.GetBitmap(&hb);
- //获取窗口大小
- CRect rt;
- GetClientRect(&rt);
- //显示位图
- pDC->StretchBlt(0, 0, rt.Width(), rt.Height(),
- &dc, 0, 0, hb.bmWidth, hb.bmHeight, SRCCOPY);
- return TRUE;
- }
- HBRUSH CUi4Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
- {
- //设置透明背景模式
- pDC->SetBkMode(TRANSPARENT);
- //设置背景刷子为空
- return (HBRUSH)::GetStockObject(HOLLOW_BRUSH);
- }
[cpp] view plaincopy
- BOOL CUi4Dlg::OnInitDialog()
- {
- //…
- //加载位图
- //CBitmap m_Back;
- m_Back.LoadBitmap(IDB_BACK);
- //…
- }
- BOOL CUi4Dlg::OnEraseBkgnd(CDC* pDC)
- {
- CDC dc;
- dc.CreateCompatibleDC(pDC);
- dc.SelectObject(&m_Back);
- //获取BITMAP对象
- BITMAP hb;
- m_Back.GetBitmap(&hb);
- //获取窗口大小
- CRect rt;
- GetClientRect(&rt);
- //显示位图
- pDC->StretchBlt(0, 0, rt.Width(), rt.Height(),
- &dc, 0, 0, hb.bmWidth, hb.bmHeight, SRCCOPY);
- return TRUE;
- }
- HBRUSH CUi4Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
- {
- //设置透明背景模式
- pDC->SetBkMode(TRANSPARENT);
- //设置背景刷子为空
- return (HBRUSH)::GetStockObject(HOLLOW_BRUSH);
- }
同时别忘了响应OnCtlColor,否则窗口里面的控件就不透明了。OnCtlColor的内容,详见3.3.3章节。
3.3.3 WM_CTLCOLOR
在控件显示之前,每一个控件都会向父对话框发送一个WM_CTLCOLOR消息要求获取绘制所需要的颜色。WM_CTLCOLOR消息缺省处理函数CWnd::OnCtlColor返回一个HBRUSH类型的句柄,这样,就可以设置前景和背景文本颜色,并为控件或者对话框的非文本区域选定一个刷子。
WM_CTLCOLOR的映射函数原型如下:
afx_msg HBRUSH OnCtlColor( CDC* pDC, CWnd* pWnd, UINT nCtlColor );
返回值:
用以指定背景的刷子
参数:
pDC指定了绘制操作所使用的设备环境。
pWnd 控件指针
nCtlColor 指定控件类型,其取值如表2所示:
类型值 含义
CTLCOLOR_BTN 按钮控件
CTLCOLOR_DLG 对话框
CTLCOLOR_EDIT 编辑控件
CTLCOLOR_LISTBOX 列表框
CTLCOLOR_MSGBOX 消息框
CTLCOLOR_SCROLLBAR 滚动条
CTLCOLOR_STATIC 静态控件
表2 nCtlColor的类型值与含义
作为一个简单的例子,观察以下的代码:
[cpp] view plaincopy
- BOOL CUi5Dlg::OnInitDialog()
- {
- //…
- //创建字体
- //CFont CUi1View::m_Font1, CUi1View::m_Font2
- m_Font1.CreatePointFont(120, "Impact");
- m_Font3.CreatePointFont(120, "Arial");
- return TRUE; // return TRUE unless you set the focus to a control
- }
- HBRUSH CUi5Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
- {
- HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
- if(nCtlColor == CTLCOLOR_STATIC)
- {
- //区分静态控件
- switch(pWnd->GetDlgCtrlID())
- {
- case IDC_STATIC1:
- {
- pDC->SelectObject(&m_Font1);
- pDC->SetTextColor(RGB(0, 0, 255));
- break;
- }
- case IDC_STATIC2:
- {
- pDC->SelectObject(&m_Font2);
- pDC->SetTextColor(RGB(255, 0, 0));
- break;
- }
- }
- }
- return hbr;
- }
[cpp] view plaincopy
- BOOL CUi5Dlg::OnInitDialog()
- {
- //…
- //创建字体
- //CFont CUi1View::m_Font1, CUi1View::m_Font2
- m_Font1.CreatePointFont(120, "Impact");
- m_Font3.CreatePointFont(120, "Arial");
- return TRUE; // return TRUE unless you set the focus to a control
- }
- HBRUSH CUi5Dlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
- {
- HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);
- if(nCtlColor == CTLCOLOR_STATIC)
- {
- //区分静态控件
- switch(pWnd->GetDlgCtrlID())
- {
- case IDC_STATIC1:
- {
- pDC->SelectObject(&m_Font1);
- pDC->SetTextColor(RGB(0, 0, 255));
- break;
- }
- case IDC_STATIC2:
- {
- pDC->SelectObject(&m_Font2);
- pDC->SetTextColor(RGB(255, 0, 0));
- break;
- }
- }
- }
- return hbr;
- }