5.3.4 消息映射
视频精讲:光盘\video\05\消息映射.swf
在MFC中,消息映射机制的基本实现思想是:首先,将每个类所感兴趣的消息都关联到其对应的消息响应函数上生成消息映射表,然后,将基础类与其派生类的消息映射表连接起来,形成更大的消息映射网,这样,当有消息发生时,通过对比MFC窗口的消息映射表,应用程序便可知当前消息是否有可执行的消息处理函数。
为了将一个类所感兴趣的消息及其消息响应函数对应起来生成消息映射表,我们定义了如下结构:
struct AFX_MSGMAP_ENTRY { UINT nMessage; //Windows 消息的ID UINT nCode; //控制消息的通知码 UINT nID; //命令消息ID范围的起始值 UINT nLastID; //命令消息ID范围的终止值 UINT nSig; //消息的动作标识 AFX_PMSG pfn; //调用的路径 };
|
有了上述结构,通过一个AFX_MSGMAP_ENTRY类型的数组_messageEntries[],就可以容纳当前类所有感兴趣的消息映射项了。
为了契合MFC的消息传递机制(即可以把自己不处理的消息传送给别的类进行处理),我们还要增加一个结构体,以根据类之间的关系把所有的AFX_MSGMAP_ENTRY数组串联起来,实现对各个MFC对象的消息映射表的查找。该结构体的定义如下:
struct AFX_MSGMAP { const AFX_MSGMAP* pBaseMap; //指向其他类的AFX_MSGMAP对象 const AFX_MSGMAP_ENTRY* lpEntries; //指向自身的消息表 }; |
通过在每个需要响应Windows消息的类中声明一个该结构的变量messageMap,并让其pBaseMap指针指向基类或另一个类的messageMap变量,就可以形成本小节开头所讲到的消息映射网,其结构如图5-4所示。
|
| (点击查看大图)图5-4 消息映射网结构 |
形成MFC对象的消息映射网之后,为了方便查找,我们还需要在想要响应Windows消息的类中插入以下两个函数。
_GetBaseMessageMap():该函数的功能是获取当前类的基类的消息映射表,其定义如下。
const AFX_MSGMAP* PASCAL theClass::_GetBaseMessageMap() \ { return &baseClass::messageMap; } \ GetMessageMap():该函数的功能是获得当前类自身的消息映射表,其定义如下。 const AFX_MSGMAP* theClass::GetMessageMap() const \ { return &theClass::messageMap; } \
|
至此,我们就构造出了在MFC中实现消息映射机制所需的数据结构及函数,通过DECLARE_MESSAGE_MAP()宏可以为每个想要实现消息映射的类提供对这些数据结构和函数的声明。DECLARE_MESSAGE_MAP()宏在afx.h头文件中的定义如下:
#define DECLARE_MESSAGE_MAP() \ private: \ static const AFX_MSGMAP_ENTRY _messageEntries[]; \ protected: \ static AFX_DATA const AFX_MSGMAP messageMap; \ virtual const AFX_MSGMAP* GetMessageMap() const; \ |
声明了所需的数据结构及函数之后,消息映射网的形成就要靠BEGIN_MESSAGE_MAP()、ON_COMMAND()和END_MESSAGE_MAP() 3个宏来实现。在afx.h头文件中,它们的定义如下:
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \ const AFX_MSGMAP* theClass::GetMessageMap() const \ { return &theClass::messageMap; } \ AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = \ { &baseClass::messageMap, &theClass::_messageEntries[0] }; \ AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \ { \ #define ON_COMMAND(id, memberFxn) \ { WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)&memberFxn }, #define END_MESSAGE_MAP() \ {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \ }; \ |
下面就以继承于CDialog类的CMyDialog类为例,讲解在响应WM_PAINT消息时以上3个宏的作用。未执行宏替换前的代码形式如下:
//*.h class CMyDialog : public CDialog { public: …… DECLARE_MESSAGE_MAP() } //*.cpp …… BEGIN_MESSAGE_MAP(CMyDialog, CDialog) ON_WM_PAINT() END_MESSAGE_MAP() |
执行宏替换之后,得到的代码如下:
//*.h class CMyDialog : public CDialog { public: …… private: static const AFX_MSGMAP_ENTRY _messageEntries[]; protected: static AFX_DATA const AFX_MSGMAP messageMap; virtual const AFX_MSGMAP* GetMessageMap() const; } //*.cpp //获得CMyDialog类的消息映射表 const AFX_MSGMAP* CMyDialog::GetMessageMap() const { return &CMyDialog::messageMap; } //关联CDialog类及CMyDialog类的消息映射表 const AFX_MSGMAP CMyDialog::messageMap = { &CDialog::messageMap, &CMyDialog::_messageEntries[0] }; //记录CMyDialog类感兴趣的消息映射,可添加多个AFX_MSGMAP_ENTRY对象 const AFX_MSGMAP_ENTRY CMyDialog::_messageEntries[] = { { WM_COMMAND, 0, (WORD)WM_PAINT, (WORD) WM_PAINT, AfxSig_vv, (AFX_PMSG)&OnPaint }, //加入WM_PAINT消息参数 {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } //消息映射的结束项}; |
【责任编辑:
云霞 TEL:(010)68476606】