VC++创建有个性的对话框之MFC篇
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
本文由小徐技术库贡献
doc文档可能在WAP端浏览体验不佳。建议您优先选择TXT,或下载源文件到本机查看。
本资料由-校园大学生创业网-提供 /
创建有个性的对话框之 MFC 篇
想使自己的软件与众不同就要给软件加点“色”, 一个颜色搭配协调的窗口 要比 windows 千篇一律的灰底黑字更能吸引别人的眼球。设想如果 html 浏览器 显示的网页都是白底黑字,还会有那么多的 mm 喜欢上网吗?可能互联网的人气 将下降一半。做个出色的界面对于老手来说可能不在话下,但是对于新手来说还 是无从下手,使用 BCGControlBar 和 Xtreme Toolkit 是个很好的选择,不过对 于一个小程序使用这么大的库未免有头重脚轻的感觉。 其实不使用这些庞然大物 一样可以做个很“色”的界面,本文就结合 CSDN 论坛上经常被问起的问题,介 绍几个给对话框上色的方法。本文的方法都是针对 MFC 程序的,其他方法请参看 “创建有个性的对话框之 ATL/WTL 篇”。 第一步:改变对话框的背景颜色 如何改变对话框的背景颜色这个问题常常出现在论坛上,可见大家对 Windows 默认的灰色对话框是多么不满。MFC 程序修改对话框的背景和文字颜色 最简单的方法就是调用 SetDialogBkColor 函数,SetDialogBkColor 是 CWinApp 类的成员函数,以下是该函数的原型: void CWinApp::SetDialogBkColor(COLORREF clrCtlBk, COLORREF clrCtlText); 请注意,SetDialogBkColor 函数并不是对 Windows 的某个 API 的封装,他 是 MFC 框架的一部分,所以不使用 MFC 的程序也就不能享受这种方便。这个函数 的使用很简单,在程序的 CWinApp 派生类的 InitInstance 函数中添加一行代码 就行了: SetDialogBkColor(RGB(188,197,230),RGB(13,125,188)); 图.1 就是运行效果:
在线代理 /提供部分资料
本资料由-校园大学生创业网-提供 /
图.1 SetDialogBkColor 效果图 使用 SetDialogBkColor 也有局限的地方,那就是所有的控件文字颜色都一 样,不能针对不同的控件设置不同的文字颜色,还有就是不能设置 Edit 控件的 颜色。不使用 SetDialogBkColor 函数,直接编写代码控制对话框的背景颜色和 控件文字颜色也不是很困难的事情, 并且这种方法能够提供更灵活的颜色设置方 案,比如对不同类型的控件使用不同的文字颜色,使用高亮度的背景颜色突出某 个控件等等,最重要的是能够控制 Edit 控件的文字和背景颜色,下面就介绍这 种方法。 首先是改变对话框的背景颜色。 Windows 系统需要重画某个窗口客户区的 当 背景的时候,就会向该窗口发送 WM_ERASEBKGND 消息,窗口的处理过程响应这 个消息重新画窗口的背景,这个过
程称之为“自画”。改变对话框的背景颜色的 原理很简单,就是响应这个消息,用自定义的颜色填充对话框的客户区背景,代 替对话框窗口默认的背景填充动作。许多新手经常问:“为什么在 class wizard 中找不到对话框的 WM_ERASEBKGND 消息, 是不是对话框没有这个消息”?其实对 话框也是窗口,它也有 WM_ERASEBKGND 消息,只是 MFC 的 class wizard 使用的 dialog 过滤器将其过滤掉了(只是在 message 窗口的显示中过滤了,并不是真 的不响应这个消息) 为的是代码编写过程中突出对话框专有的消息和控件事件。 , 如图.2 所示,只要在 class wizard 中的“class info” table 标签下将消息 过滤器改成 Windows 就可以在对话框的消息列表中看到 WM_ERASEBKGND 了。
图.2 修改消息过滤器 现在通过 class wizard 添加 WM_ERASEBKGND 的消息响应函数,并如下所示 修改这个函数:
在线代理 /提供部分资料
本资料由-校园大学生创业网-提供 /
BOOL CCustDlgDlg::OnEraseBkgnd(CDC* pDC) { CRect rcClient; GetClientRect(&rcClient); pDC->FillRect(&rcClient,&m_brBkgnd); return TRUE; //return CDialog::OnEraseBkgnd(pDC); } m_brBkgnd 是个 CBrush,在此之前已经初始化过了,关键代码是最后返回 TRUE,而不是默认的调用基类函数,返回 TRUE 意在告诉 Windows:“我已经画 过背景了,你不要再画了”。现在来看看运行的效果:
图.3 重画背景的效果 使用位图作为对话框的背景也不难,就是在整个客户区画一个位图背景, 第二步:改变控件的颜色 看起来不如刚才效果好,控件文字的颜色和背景色都没有改变,这是因为我 们还没有处理 WM_CTLCOLOR 消息。WM_CTLCOLOR 是 Windows 的控件向其父窗口发 送最频繁的通知消息之一,例如,许多控件发送 WM_CTLCOLOR 消息给父窗口,让 父窗口提供画刷来画自己的背景。MFC 的窗口类对这个通知消息特殊对待,如果
在线代理 /提供部分资料
本资料由-校园大学生创业网-提供 /
父窗口没有处理这个通知消息,MFC 的窗口类就根据 WM_CTLCOLOR 通知消息的来 源将这个 WM_CTLCOLOR 消息发送回控件,让控件自己处理,这就是所谓的“消息 反射”,不仅是 WM_CTLCOLOR,MFC 对很多通知消息都做了反射,不过我们今天 的例子没有使用“消息反射”,我们在控件的父窗口,也就是对话框窗口处理这 个通知消息。还有一点需要说明的是,WM_CTLCOLOR 消息是 16 位的 Windows 平 台的消息,在 32 位的 Windows 平台上取而代之的是一系列更明确的通知消息: WM_CTLCOLORBTN 按钮控件 WM_CTLCOLORDLG 对话框 WM_CTLCOLOREDIT 编辑控件 WM_CTLCOLORLISTBOX 列表框控件 WM_CTLCOLORSCROLL
BAR 滚动条控件 WM_CTLCOLORSTATIC 静态文本控件 MFC 为了兼容性考虑,仍旧使用 OnCtlColor 响应这些消息,但是通过参数 nCtlColor 来具体的区分他们。在这个函数中,我们可以通过改变 pDC 参数的属 性来改变控件的绘制,并返回相应的画刷句柄给控件,控件使用这个画刷画自己 的背景。下面是我们修改后的 OnCtlColor 函数: HBRUSH CCustDlgDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); pDC->SetTextColor(m_clrText); pDC->SetBkMode(TRANSPARENT); return (HBRUSH)m_brBkgnd; //因为 CBrush 类实现了 HBRUSH 类型转换操作 符 //return hbr; } 图.4 就是这段代码的效果,在这里我们不分“青红皂白”,向所有的控件 返回我们自己的画刷,看起来不错,Edit 控件的文字颜色也改了,但是好像多 行 Edit 控件有了麻烦,看来需要对多行 Edit 控件特殊对待。
在线代理 /提供部分资料
本资料由-校园大学生创业网-提供 /
图.4 重载 OnCtlColor 之后的效果 对于多行 Edit 控件特殊处理,如下所示,上面的问题解决了: HBRUSH CCustDlgDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); if(pWnd->GetDlgCtrlID() == IDC_EDIT_MULTI_LINE) //IDC_EDIT_MULTI_LINE 是多行 Edir 控件的 ID { pDC->SetTextColor(m_clrText); return hbr; } else { pDC->SetTextColor(m_clrText);
在线代理 /提供部分资料
本资料由-校园大学生创业网-提供 /
pDC->SetBkMode(TRANSPARENT); return (HBRUSH)m_brBkgnd; } } 上面的代码解决了 IDC_EDIT_MULTI_LINE 的问题,但是对每个多行 Edit 控 件都要判断 ID,下面的方法可以一劳永逸地解决多行编辑控件的问题: HBRUSH CCustDlgDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); TCHAR szClassName[64]; ::GetClassName(pWnd->GetSafeHwnd(),szClassName,64); if(lstrcmpi(szClassName,_T("Edit")) == 0) //是 Edit 控件 { DWORD dwStyle = pWnd->GetStyle(); if((dwStyle & ES_MULTILINE) == ES_MULTILINE) //多行 edit 控件 { pDC->SetTextColor(m_clrText); return hbr; } else { pDC->SetTextColor(m_clrText); pDC->SetBkMode(TRANSPARENT); return (HBRUSH)m_brBkgnd;
在线代理 /提供部分资料
本资料由-校园大学生创业网-提供 /
} } else //不是编辑控件 { pDC->SetTextColor(m_clrText); pDC->SetBkMode(TRANSPARENT); return (HBRUSH)m_brBkgnd; } } 下面我们针对每个控件设置特殊的颜色,区分控件可以通过控件的 ID,修 改控件背景也很简单,直接返回相应的画刷就可以了,下面就是颜色设置的完整 代码: HBRUSH CCustDlgDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CDialog:
:OnCtlColor(pDC, pWnd, nCtlColor); TCHAR szClassName[64]; ::GetClassName(pWnd->GetSafeHwnd(),szClassName,64); if(lstrcmpi(szClassName,_T("Edit")) == 0) //是 Edit 控件 { DWORD dwStyle = pWnd->GetStyle(); if((dwStyle & ES_MULTILINE) == ES_MULTILINE) //多行 edit 控件 { pDC->SetTextColor(m_clrText); return hbr;
在线代理 /提供部分资料
本资料由-校园大学生创业网-提供 /
} else { pDC->SetTextColor(m_clrText); pDC->SetBkMode(TRANSPARENT); return (HBRUSH)m_brBkgnd; } } else //不是编辑控件 { if(pWnd->GetDlgCtrlID() == IDC_STC_REDTEXT) { pDC->SetTextColor(RGB(255,0,0)); pDC->SetBkMode(TRANSPARENT); return (HBRUSH)m_brBkgnd; } else if(pWnd->GetDlgCtrlID() == IDC_STC_BLUETEXT) { pDC->SetTextColor(RGB(0,0,255)); pDC->SetBkMode(TRANSPARENT); return (HBRUSH)m_brBkgnd; } else if(pWnd->GetDlgCtrlID() == IDC_STC_BLUETEXTWHITEBACK)
在线代理 /提供部分资料
本资料由-校园大学生创业网-提供 /
{ pDC->SetTextColor(RGB(0,0,255)); pDC->SetBkMode(TRANSPARENT); return (HBRUSH)m_brControlBkgnd1; } else if(pWnd->GetDlgCtrlID() == IDC_CHK_GREEN) { pDC->SetTextColor(RGB(0,255,0)); pDC->SetBkMode(TRANSPARENT); return (HBRUSH)m_brBkgnd; } else if(pWnd->GetDlgCtrlID() == IDC_RAD_BLUE) { pDC->SetTextColor(RGB(0,0,255)); pDC->SetBkMode(TRANSPARENT); return (HBRUSH)m_brBkgnd; } else if(pWnd->GetDlgCtrlID() == IDC_CHK_GREEN2) { pDC->SetTextColor(RGB(0,255,0)); pDC->SetBkMode(TRANSPARENT); return (HBRUSH)m_brControlBkgnd2; }
在线代理 /提供部分资料
本资料由-校园大学生创业网-提供 /
else if(pWnd->GetDlgCtrlID() == IDC_RADIO2) { pDC->SetTextColor(RGB(0,0,255)); pDC->SetBkMode(TRANSPARENT); return (HBRUSH)m_brControlBkgnd2; } else { pDC->SetTextColor(m_clrText); pDC->SetBkMode(TRANSPARENT); return (HBRUSH)m_brBkgnd; } } } 现在看看效果:
在线代理 /提供部分资料
本资料由-校园大学生创业网-提供 /
图.5 修改 OnCtlColor 之后的效果 上面的代码是根据控件 ID 来设置颜色,还可以根据控件的类型统一设置某 种控件的颜色,这就要用到 nCtlColor 参数,nCtlColor 参数用来指明发送这个 通知消息的控件的类型,nCtlColor 可以是以下取值: CTLCOLOR_BTN CTLCOLOR_DLG CTLCOLOR_EDIT CTLCOLOR_LISTBOX CTLCOLOR_MSGBOX CTLCOLOR_SCROLLBAR CTLCOLOR_STATIC 第三步:使用位图作对话框的背景 使用位图作为对话框的背景也很简单,就是在 OnEraseBkgnd 中用位图填充 客户区,只是在 OnCtlColor 中需要注意返回空画刷代替原来的画刷,返回空画 刷是为了阻止控件绘制自己的背景,从而破坏位图背景的完整性,但是有时候返 回空画刷会对其他控件产生不良影响,所以我们只处理了 CTLCOLOR_BTN 和 CTLCOLOR_STATIC 两种
类型的消息:
在线代理 /提供部分资料
本资料由-校园大学生创业网-提供 /
HBRUSH CBmpBkgndDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor) { HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor); if(nCtlColor == CTLCOLOR_BTN || nCtlColor == CTLCOLOR_STATIC) { pDC->SetTextColor(RGB(0,0,255)); pDC->SetBkMode(TRANSPARENT); return (HBRUSH)m_HollowBrush; } pDC->SetTextColor(RGB(0,0,255)); pDC->SetBkMode(TRANSPARENT); return hbr; } 下面是使用位图背景和空画刷的效果:
图.6 使用位图背景的效果
在线代理 /提供部分资料
本资料由-校园大学生创业网-提供 /
第四步:单独处理按钮控件 现在看来按钮控件还是影响整体效果,WM_CTLCOLORBTN 好像对于 push button 类型的按钮控件没有效果,不过 push button 也是支持自画的,在使用 自画按钮之前, 我们先来看看控件自画的原理。 Windows 的控件都有默认的外观, 但是许多控件有支持“自画”,也就是让用户定制控件的外观,当给一个控件指 定自画的样式之后,控件在重画自己的时候向父窗口发送 WM_MEASUREITEM 和 WM_DRAWITEM 消息,父窗口响应这两个消息,定位控件的大小并绘制控件,从而 使控件有定制的外观。但是每个控件的自画都由父窗口完成加重了父窗口的负 担,也不利于代码重用,所以,MFC 对这些消息进行了反射处理,就是将消息发 还位控件,由控件响应消息,自己绘制,这样将自画代码封装在控件类中,提高 了代码的重用性。很多 MFC 的控件类都自己处理这两个消息,派生类可以重载 MeasureItem 和 DrawItem 自己画控件的外观,CButton 就是这样的控件类。 现在就来做一个自画的按钮类,首先从 CButton 派生一个类,我们命名为 CSMButton, 然后重载 DrawItem 和 PreSubclassWindow, 重载 PreSubclassWindow 的原因是在 CSMButton 子类化按钮控件之前先给按钮添加 BS_OWNERDRAW 样式, 否则按钮就不会向父窗口发送 WM_DRAWITEM 消息,MFC 的消息反射就不会发生, 我们的 DrawItem 就不会被调用,嗯,后果很严重。当然也可以让 CSMButton 的 使用者自己给按钮添加 BS_OWNERDRAW 样式,但是会让人觉得没水平,嗯,后果 也很严重。接下来添加对 WM_CAPTURECHANGED、WM_MOUSEMOVE、WM_SETCURSOR 和 WM_KILLFOCUS 四个消息的响应函数,对这四个消息的响应是为了给按钮增加 更多的功能,比如使按钮看起来象工具栏的按钮,改变鼠标的形状等等。 关于 CSMButton 类的使用就像 CButton 一样,为按钮添加变量就行了,演示 代码中包含了这个类的源代码以及用法,这里不在赘述。CSMButton 类的功能很 简单,但是完成了一个自画按钮的框架,大家可以修改代码
实现自己的风格,网 上也有很多这样的类,功能更强大,比如 STButton 等。现在看看 CSMButton 的 效果:
在线代理 /提供部分资料
本资料由-校园大学生创业网-提供 /
图.7 使用自画按钮后的效果 第五步:使用 Picture Box 控件 想要在对话框上显示位图,可以使用很复杂的控件或 CxImage 之类的库,也 可以很简单地使用 Picture Box。Picture Box 默认的样式使 Frame,需要手工 改成 Bitmap,如下图所示:
图.8 使用位图 VC6 的集成环境不支持 24 位位图的浏览和编辑,但是并不影响使用,本例 使用的位图都是 24 位的,为的是省去调色板的处理,本人比较懒。使用如下代 码就可以更改 Picture Box 中的位图:
在线代理 /提供部分资料
本资料由-校园大学生创业网-提供 /
m_hCat1 = (HBITMAP)::LoadImage(AfxGetResourceHandle(),MAKEINTRESOURCE(IDB_BITMA P1),IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION); GetDlgItem(IDC_STC_PICTURE)->SendMessage(STM_SETIMAGE,IMAGE_BITMA P, (LPARAM)m_hCat1); 装载位图还可以这样: m_hCat1 = (HBITMAP)::LoadImage(AfxGetResourceHandle(),(LPCTSTR)IDB_BITMAP1,IMAG E_BITMAP,0,0,LR_CREATEDIBSECTION); 这是最终的效果:
图.9 对话框的最终效果
在线代理 /提供部分资料