MFC中用户自定义类响应自定义消息
这篇技术文章不是讨论经典的MFC中的消息工作机理的,讨论消息工作原理、方式和路径的文章在网上和书本中随处可见。网上众多的讨论都是关于如何响应并进行用户自定义消息映射的;网上还有一些文章介绍如何在自定义类中响应Windows消息,在本文中都简略叙述。但是,网上大部分的文章没用透彻阐述如何在用户自定义类中响应自定义消息这一通用方法。
问题定义如下:用户自定义一个类,这个类不一定要有界面(完全可以是不可视的),要求自定义的类可以响应某个自定义消息。
首先能够响应消息的类必须都从CCmdTarget类中派生,因为只有以这个类中提供了消息的框架和处理机制,而CWnd类也派生与此类。CWinApp类、CDocument类、CDocTemplate类等都是CmdTarget的派生类,即子类;而CFrameWnd类、CView类、CDialog类等都是从CWnd中派生的,其实也是CCmdTarget的子孙,所以都能够响应消息,但是响应消息的种类不太相同。那么,如果自己定义的类要求响应命令消息(就是WM_COMMAND,也就是一些菜单、工具栏中的消息,包括快捷键,这类消息处理的机制与其他以WM_开头的消息处理机制不同,它具有一条层次明确的消息流动路径),那么自定义的类可以从CCmdTarget中派生。由于CWnd窗体类派生于CCmdTarget父类,那么从CWnd中派生的类也可以理所应当的响应命令消息。这种命令消息无论是往已有的一些诸如CWinApp类中还是自定义的类中添加都是一件非常容易的事情,只需用向导即可,在此不再叙述。
如果用户自定义的类要求响应普通的Windows消息(也就是以WM_开头,除了WM_COMMAND以外的消息,这类消息在WM_USER以下的是系统消息,WM_USER以上的可以由用户自己定义),那就要求自定义的类必须从CWnd中派生。这是由于此类消息的处理机制决定的,这类消息没有命令消息那条繁琐的流动路径,而是消息发出者直接发给对应CWnd的窗体句柄,由CWnd负责消息的响应。所以这类消息必须同一个CWnd类对应,更精确的说必须与一个HWND类型的窗体句柄相对应。这样得出一个重要的结论,就是从CCmdTarget中派生而没有从CWnd派生的类没有处理此类消息的能力。
综上所述,就是为什么命令消息可以放到大部分类中处理,包括CWinThread、CWinApp、CDocument、CView、CFrameWnd或是自定义的类中,而普通Windows消息和用户自定义的消息只能放到CFrameWnd和CView等派生与CWnd中的类中处理。
由此可见,我们自定义的类要想响应自定义消息就只能从CWnd中派生(当然不响应任何消息的类可以从CObject中派生)。先来看看如何自定义消息:
在.h中做的工作:
第一步要声明消息:
#define WM_MYMSG WM_USER+8
第二步要在类声明中声明消息映射:
DECLARE_MESSAGE_MAP()
第三步要在类声明中定义消息处理函数:
afx_msg LRESULT MyMsgHandler(WPARAM,LPARAM);
在.cpp中做的工作:
第四步要实现消息映射:
BEGIN_MESSAGE_MAP(CMainFrame, CMDIFrameWnd)
ON_MESSAGE(WM_MYMSG,OnMyMsgHandler)
END_MESSAGE_MAP()
第五步要实现消息处理函数(当然可以不实现):
LRESULT CMainFrame::OnMyMsgHandler(WPARAM w,LPARAM l)
{
AfxMessageBox("Hello,World!");
return ;
}
在引发或发出消息的地方只用写上:
::SendMessge(::AfxGetMainWnd()->m_hWnd,WM_MYMSG,,);
到此,自定义消息完毕,这是好多网上文章都写的东西。大家会发现上面代码是在CMainFrame类中实现的,但是如果要用自定义类,就没有那么简单了。显然把第四步与第五步的CMainFrame换成自定义的类名(这里我用CMyTestObject来代表自定义类)是不能正常工作的。原因在于在发送消息的SendMessage函数中的第一个参数是要响应消息对应的HWND类型的窗体句柄,而CMyTestObject类中的m_hWnd中在没有调用CWnd::Create之前是没有任何意义的,也就是没有调用CWnd::Create或CWnd::CreateEx函数时,CWnd不对应任何窗体,消息处理不能正常运作。
所以,又一个重要的结论,在自定义类能够处理任何消息之前一定要确保m_hWnd关联到一个窗体,即便这个窗体是不可见的。那么有人说,在自定义类的构造函数中调用Create函数就行了,不错,当然也可以在别处调用,只要确保在消息发送之前。但是,Create的调用很有说法,要注意两个地方,第一个参数是类的名称,我建议最好设为NULL;第五个参数是父窗体对象的指针,这个函数指定的对象一定要存在,我建议最好为整个程序的主窗体。还有很多人问第六个参数的意义,这个参数关系不大,是子窗体ID,用于传给父窗体记录以便识别。如下是我的自定义类的构造函数:
CMyTestObject::CMyTestObject()
{
CWnd::Create(NULL,"MyTestObject",WS_CHILD,CRect(,,,),::AfxGetMainWnd(),);
} //一定要在生成主窗体后使用,在主窗体完成OnCreate消息的处理后
CMyTestObject::CMyTestObject(CWnd *pParent)
{
CWnd::Create(NULL,"MyTestObject",WS_CHILD,CRect(,,,),pParent,);
}
不能如下调用Create,因为此时CMyTestObject不关联任何窗体,所以this中的m_hWnd无效:
CWnd::Create(NULL,"MyTestObject",WS_CHILD,CRect(,,,),this,);
这时上面四、五两步修改成:
BEGIN_MESSAGE_MAP(CMyTestObject, CWnd)
ON_MESSAGE(WM_MYMSG,OnMyMsgHandler)
END_MESSAGE_MAP()
LRESULT CMyTestObject::OnMyMsgHandler(WPARAM w,LPARAM l)
{
AfxMessageBox("My Messge Handler in My Self-Custom Class!");
return ;
}
在类外部发出消息:
CMyTestObject *test=new CMyTestObject();
::SendMessage(test->m_hWnd,WM_MYMSG,,);
在类内部某个成员函数(方法)中发出消息:
::SendMessage(m_hWnd,WM_MYMSG,,);
最后一个问题便是容易产生警告错误的窗体回收,自定义的类要显式调用窗体销毁,析构函数如下:
CMyTestObject::~CMyTestObject()
{
CWnd::DestroyWindow();
}
MFC中用户自定义类响应自定义消息的更多相关文章
- 列表控件ListBox关联的MFC中的类:CListBox
列表控件ListBox关联的MFC中的类:CListBox ######################################################## 1.在列表的结尾添加一项: ...
- 高级列表控件ListCtrl关联的MFC中的类:CListCtrl
高级列表控件ListCtrl关联的MFC中的类:CListCtrl■ 报表样式ListCtrl常用操作:1.添加列标题头:InsertColumn2.获取与设置列宽:GetColumnWidth.Se ...
- MFC中对话框类(Dialog)的应用
转载http://hi.baidu.com/jackywdx/item/feee8041d2c2e12310ee1e85 Windows应用程序通常是通过对话框接收用户输入.向用户输出信息,本节介绍应 ...
- MFC中 自定义类访问主对话框控件的方法
之前一直在找有木有好点的方法.现在终于被我找到,收藏之~~~~~~ 在使用mfc的时候经常遇到自定义类访问主对话框控件的问题,例如自定义类中的方法要输出一段字符串到主对话框的EDIT控件.控制对话框的 ...
- MFC中CArray类原理及其应用
1.CArray类应用 函数简介CArray::GetSize int GetSize( ) const;取得当前数组元素个数. CArray::GetUpperBound int GetUpperB ...
- MFC中Doc类获取View类的方法(SDI)
从view类中获取Doc的方法如下: CYourDoc* pDoc = GetDocument(); 这个函数已经写好,所以无需自己添加,使用时直接利用pDoc即可. 若反过来,从Doc中获取View ...
- MFC中CListCtrl类依靠CImageList贴图并显示不同图像
只介绍主要方法,函数的具体参数可在MSDN上查阅 ------------------------------------------- CListCtrl m_ListCtrl; CImag ...
- MFC 中的设计模式分析
MFC 中的设计模式分析 最近在学习设计模式,突然想到MFC里面其实也包含有设计模式的原理,于是分析了一下,做一个笔记,网上也找了一些资料,在此一并感谢. 创建型模式 单例模式(Singleton P ...
- MFC中CTime获取日期时间的方法
MFC中CTime类的功能非常强大,可以获取年.月.日.小时.分钟.秒.星期等等,最最重要的是可根据需要去格式化.下面是具体的使用方式: ① 定义一个CTime类对象 CTime time; ② 得到 ...
随机推荐
- Java - 避免不必要的对象
通常,我们更喜欢重用一个对象而不是重新创建一个.如果对象是不可变的,它就始终可以被重用. 下面是一个反面例子: String s = new String("stringette" ...
- Oracle OCI操作UDT相关学习(二)
沿用 Oracle OCI操作UDT相关学习 一文中定义的类型和表. 1.更改数据 在sqldeveloper 中更新数据, update dxl.cust set addr.street='a11' ...
- springboot自定义异常
SpringBoot自定义异常以及异常处理 在web项目中,我们可能需要给前端返回不同的提示码.例如:401表示没有权限,500代表位置异常,200代表请求成功等.但是这些提示码远远不能满足我们返回给 ...
- 深入理解jQuery插件开发总结(一)
由于这篇文章比较长,所以分了四个阶段讲,从简单的入门级到最后到综合级,有些列子和图片都是转载其他博主的,希望对想写插件对同学会有帮助.这里分享个好久之前写的一个jquery插件(网站的功能引导插件,思 ...
- 【转】Dom节点操作常用方法
1.访问/获取节点 document.getElementById(id); //返回对拥有指定id的第一个对象进行访问 document.getElementsByName(name); //返回带 ...
- java 单例的实现及多线程下的安全
package com.demo01; public class Single { /** * 设计模式:单例设计模式 * 解决一个类中只允许存在一个对象这种情况: * 不允许通过类,无限制的创建该类 ...
- js event事件对象概括
事件是用户或者浏览器自身执行的动作,而响应某个事件的函数就叫做事件处理程序或者叫事件侦听器. 定义事件处理程序可以大致分为以下三种: 一.html事件处理程序 元素支持的每种事件都可以用一个与之对应的 ...
- Thrift-RPC client in Flume
Get RpcClient from RpcClientFactory with Reflection programming Message or Event definition in Flum ...
- css 三角形的制作
参考网页: http://www.jb51.net/article/42513.htm 1 .设置宽高为0 2 .设置4条边框 3 .设置边框颜色border-color如下: border-colo ...
- Google play billing(Google play 内支付) 下篇
开篇: 如billing开发文档所说,要在你的应用中实现In-app Billing只需要完成以下几步就可以了. 第一,把你上篇下载的AIDL文件添加到你的工程里,第二,把 <uses-perm ...