没有找到合适的产品?
联系客服协助选型:023-68661681
提供3000多款全球软件/控件产品
针对软件研发的各个阶段提供专业培训与技术咨询
根据客户需求提供定制化的软件开发服务
全球知名设计软件,显著提升设计质量
打造以经营为中心,实现生产过程透明化管理
帮助企业合理产能分配,提高资源利用率
快速打造数字化生产线,实现全流程追溯
生产过程精准追溯,满足企业合规要求
以六西格玛为理论基础,实现产品质量全数字化管理
通过大屏电子看板,实现车间透明化管理
对设备进行全生命周期管理,提高设备综合利用率
实现设备数据的实时采集与监控
利用数字化技术提升油气勘探的效率和成功率
钻井计划优化、实时监控和风险评估
提供业务洞察与决策支持实现数据驱动决策
翻译|其它|编辑:郝浩|2004-01-16 09:31:00.000|阅读 1862 次
概述:
# 界面/图表报表/文档/IDE等千款热门软控件火热销售中 >>
作为一个程序员,我们经常会在程序中用到Windows通用控件。比如按钮控件,进度条控件等等。但是有时我们需要给控件更多的特色,这就需要做控件的子类化(subclassing).
子类化一个Windows控件与子类化一个C++类不同,子类化一个控件要求你把一个窗口的一些或所有的消息映射都替换成自己的函数来响应,这样你就有效的阻止了控件去做系统默认的行为,而按自己的想法去做。子类化有两种类型: 实例子类化(instance subclassing)和全局子类化(global subclassing)。实例子类化是子类化一个窗口中的单一实例,全局子类化是把整个窗口子类化为一个特殊的类型。这里我们仅讨论单一实例子类化。
记住CWnd
派生类对象与窗口本身(一个HWND
)的差别是很重要的。你的C++ CWnd
-派生类对象包含了一个指向HWND
的成员函数,并且包含了当处理消息时HWND
消息泵的响应函数(比如WM_PAINT
, WM_MOUSEMOVE
)。但你用一个C++对象子类化一个窗口时,你就把HWND
与C++对象关联起来,并且设置了处理消息时把自定义的回调函数提供给HWND
消息使用。
子类化过程很简单,首先创建一个类映射窗口的所有消息,然后把控件用作为这个类的实例。例如,下面的例子中我们做一个按钮的子类化。
为了子类化一个控件,我们需要创建一个新类,并映射所有我们感兴趣的消息。为了简便,我们一般都从控件标准类中派生自己的新类,这里与按钮控件对应的标准类为CButton。
下面假定我们要实现的效果是,当鼠标悬停在按钮上方时,按钮显示为黄色。首先我们使用ClassWizard创建一个CButton
的派生类,叫做CMyButton
。
在MFC框架中从CButton
派生自己的类有许多好处,最大的好处是我们不用手工添加任何一行代码就可以创建了一个拥有全部默认功能的Windows控件。因为MFC实现了所有的默认的消息映射,因此我们可以挑选我们感兴趣的消息自己处理,而不用去管其他消息。
这里我们要为按钮设计的功能是,鼠标悬停时变为黄色。
为了检查鼠标是否悬停于按钮上,我们设置一个成员变量m_bOverControl ,TRUE表示鼠标悬停,然后设置一个周期(使用定时器)跟踪鼠标是否已离开控件,这是因为,系统并没有OnMouseEnter
和OnMouseLeave
函数供我们调用,因此我们必须使用OnMouseMove
。如果,在一个时间点上,发现鼠标已离开按钮,我们关闭定时器并重画控件。
使用ClassWizard加入WM_MOUSEMOVE和WM_TIMER的消息映射,响应函数分别是OnMouseMove
和OnTimer
。
ClassWizard将在你的按钮类文件中加入下面的代码:
BEGIN_MESSAGE_MAP(CMyButton, CButton) //{{AFX_MSG_MAP(CMyButton) ON_WM_MOUSEMOVE() ON_WM_TIMER() //}}AFX_MSG_MAP END_MESSAGE_MAP() ///////////////////////////////////////////////////////////////////////////// // CMyButton message handlers void CMyButton::OnMouseMove(UINT nFlags, CPoint point) { // TODO: Add your message handler code here and/or call default CButton::OnMouseMove(nFlags, point); } void CMyButton::OnTimer(UINT nIDEvent) { // TODO: Add your message handler code here and/or call default CButton::OnTimer(nIDEvent); }
消息映射的入口(即BEGIN_MESSAGE_MAP
) 建立了窗口消息与响应函数的对应关系。ON_WM_MOUSEMOVE
把WM_MOUSEMOVE消息与OnMouseMove
函数建立响应的关系,ON_WM_TIMER
m把WM_TIMER消息与OnTimer
函数建立了响应的关系。这些宏定义在MFC的源文件中,我们不需要去看,只要按照约定来做就可以了。
假设我们已经声明了两个变量m_bOverControl和m_nTimerID,类型分别是BOOL和UINT, 并且在类的构造函数中把它们初始化,我们的消息处理应使用下面的代码:
void CMyButton::OnMouseMove(UINT nFlags, CPoint point) { if (!m_bOverControl) // Cursor has just moved over control { TRACE0("Entering controln"); m_bOverControl = TRUE; // Set flag telling us the mouse is in Invalidate(); // Force a redraw SetTimer(m_nTimerID, 100, NULL); // Keep checking back every 1/10 sec } CButton::OnMouseMove(nFlags, point); // drop through to default handler } void CMyButton::OnTimer(UINT nIDEvent) { // Where is the mouse? CPoint p(GetMessagePos()); ScreenToClient(&p); // Get the bounds of the control (just the client area) CRect rect; GetClientRect(rect); // Check the mouse is inside the control if (!rect.PtInRect(p)) { TRACE0("Leaving controln"); // if not then stop looking... m_bOverControl = FALSE; KillTimer(m_nTimerID); // ...and redraw the control Invalidate(); } // drop through to default handler CButton::OnTimer(nIDEvent); }
最后我们来画出我们需要的效果,我们不再进行消息映射,而是重载CWnd::DrawItem
虚函数。只有当控件设置owner-drawn风格时这个函数才能被调用,并且这个函数没有默认的实现代码,虚函数的设计只为了在派生类中进行实现。
使用ClassWizard重载DrawItem
函数,并加入下面的代码
void CMyButton::DrawItem(LPDRAWITEMSTRUCT lpDrawItemStruct) { CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC); CRect rect = lpDrawItemStruct->rcItem; UINT state = lpDrawItemStruct->itemState; CString strText; GetWindowText(strText); // draw the control edges (DrawFrameControl is handy!) if (state & ODS_SELECTED) pDC->DrawFrameControl(rect, DFC_BUTTON, DFCS_BUTTONPUSH | DFCS_PUSHED); else pDC->DrawFrameControl(rect, DFC_BUTTON, DFCS_BUTTONPUSH); // Deflate the drawing rect by the size of the button’s edges rect.DeflateRect( CSize(GetSystemMetrics(SM_CXEDGE), GetSystemMetrics(SM_CYEDGE))); // Fill the interior color if necessary if (m_bOverControl) pDC->FillSolidRect(rect, RGB(255, 255, 0)); // yellow // Draw the text if (!strText.IsEmpty()) { CSize Extent = pDC->GetTextExtent(strText); CPoint pt( rect.CenterPoint().x - Extent.cx/2, rect.CenterPoint().y - Extent.cy/2 ); if (state & ODS_SELECTED) pt.Offset(1,1); int nMode = pDC->SetBkMode(TRANSPARENT); if (state & ODS_DISABLED) pDC->DrawState(pt, Extent, strText, DSS_DISABLED, TRUE, 0, (HBRUSH)NULL); else pDC->TextOut(pt.x, pt.y, strText); pDC->SetBkMode(nMode); } }
接下来,我们剩下最后一步。为控件设置owner drawn风格。我们可以在对话框的资源编辑器中,右键单击按钮控件,选择“属性”,然后在Style中选中owner drawn风格。但是有一种更好的方法,使得使用新建类子类化的按钮自动的设置owner drawn风格。为了完成这个功能,我们重载最后一个函数: PreSubclassWindow
。
这个函数将在子类化窗口时被调用,次序是在CWnd::Create
或DDX_Control
之后,这就是说,无论是动态的创建窗口实例还是使用对话框模板创建,这个函数都将被调用。PreSubclassWindow
在窗口子类化创建后和窗口被显示前被调用,换句话说,这是我们来做窗口初始化的一个最好时机。
一个重点要注意的地方是: 如果你是用对话框资源创建一个控件,那么你要子类化的控件将不会响应WM_CREATE消息,所以我们不能在OnCreate
函数中做初始化的工作,因为它并不是在所有的情况下都被调用。
使用ClassWizard重载PreSubclassWindow
函数并加入下面的代码
void CMyButton::PreSubclassWindow() { CButton::PreSubclassWindow(); ModifyStyle(0, BS_OWNERDRAW); // make the button owner drawn }
祝贺 - 你的Cbutton
派生类已经完成。
在这个例子中,我们使用对话框编辑器在对话框中加入了一个新的按钮:
然后,使用ClassWizard为你的按钮控件添加成员变量,变量类型选择我们刚刚建立的类CMyButton
ClassWizard g会在对话框的DoDataExchange
函数中创建一个DDX_Control
调用。DDX_Control
启动了子类化过程,使得按钮控件使用CMyButton
类进行消息映射,而不是使用通常的CButton
。
如果你在工程中加入了一个新的窗口类,并且希望使用这个新类类型子类化你的窗口,但是ClassWizard中并没有提供新类的选项,那么你需要重新生成class wizard文件。
先备份以下工程中的.clw文件,然后删除它。接下来在Visual Studio中按Ctrl+W。你将看到一个提示框,要求你加入ClassWizard中包含类的文件,确认选择的文件中包含了新类的文件(soarlove注:一般情况下,选择“add all”即可。
现在你的新类已经可以供选择。如果不想这样做,你还有一个通用的方法,就是在选择类型的时候使用通用的类(比如CButton
),然后在头文件中手工把通用类(CButton)改为你的新类(CMyButton
)。
使用DDX固然简单,但是不能帮助我们实现一个已存在窗口的子类化。比如你想在combobox中子类化一个Edit控件,那么在你子类化Edit控件之前,你需要先创建combobox控件。
这种情况下,我们使用SubclassDlgItem
或者SubclassWindow
函数。这两个函数允许你动态的子类化一个窗口,换句话说,把一个新的窗口实例与已经存在的窗口建立关联。
比如,假设有一个对话框中包含了一个按钮ID IDC_BUTTON1
。这个按钮已经被创建,我们想用一个CMyButton
的实例来与之关联,以使得按钮符合我们需要的行为。
为了做到这些,我们需要有一个新类型的实例,最后的方法是在对话框或视的头文件中加入成员函数。
CMyButton m_btnMyButton;
然后在对话框的OnInitDialog
(或任何适当的地方) 中调用:
m_btnMyButton.SubclassDlgItem(IDC_BUTTON1, this);
假设你已经有了一个窗口的指针,或者你工作在一个CView
或其他CWnd
派生类中里面的控件被动态的创建,或者你不想使用SubclassDlgItem
函数,那么你可以使用下面的方法:
CWnd* pWnd = GetDlgItem(IDC_BUTTON1); // or use some other method to get // a pointer to the window you wish // to subclass ASSERT( pWnd && pWnd->GetSafeHwnd() ); m_btnMyButton.SubclassWindow(pWnd->GetSafeHwnd());
画按钮是非常简单的,不需要考虑按钮的风格(比如flat风格),也不需要考虑适应文字,仅仅需要考虑你画的范围。如果你编译运行提供的演示代码,那么你将看到,当鼠标悬停于按钮上方时,按钮变为黄色。
注意,实际上我们只重载了画的函数,并截取了鼠标移动的函数。其余的功能都还是使默认响应的。
子类化并不难 - 你只要认真的选择你要子类化的类并且知道你要映射那些消息。要熟悉你要子类化的类,了解提供的消息和类中的虚函数。
本站文章除注明转载外,均为本站原创或翻译。欢迎任何形式的转载,但请务必注明出处、不得修改原文相关链接,如果存在内容上的异议请邮件反馈至chenjj@evget.com
面对“数字中国”建设和中国制造2025战略实施的机遇期,中车信息公司紧跟时代的步伐,以“集约化、专业化、标准化、精益化、一体化、平台化”为工作目标,大力推进信息服务、工业软件等核心产品及业务的发展。在慧都3D解决方案的实施下,清软英泰建成了多模型来源的综合轻量化显示平台、实现文件不失真的百倍压缩比、针对模型中的大模型文件,在展示平台上进行流畅展示,提升工作效率,优化了使用体验。
本站的模型资源均免费下载,登录后即可下载。模型仅供学习交流,勿做商业用途。
本站的模型资源均免费下载,登录后即可下载。模型仅供学习交流,勿做商业用途。
本站的模型资源均免费下载,登录后即可下载。模型仅供学习交流,勿做商业用途。
服务电话
重庆/ 023-68661681
华东/ 13452821722
华南/ 18100878085
华北/ 17347785263
客户支持
技术支持咨询服务
服务热线:400-700-1020
邮箱:sales@evget.com
关注我们
地址 : 重庆市九龙坡区火炬大道69号6幢
慧都科技 版权所有 Copyright 2003-
2025 渝ICP备12000582号-13 渝公网安备
50010702500608号