事件表通常是某种类型的 struct,其中包含事件消息标识符以及指向要处理事件消息的函数的指针。
这种类型的数据结构在许多 GUI 框架中相当普遍。例如,Microsoft MFC 框架使用它。
事件表是实际的数据结构,不是 C++ 编程语言的一部分。您使用 C++ 来定义事件表。
使这种方法发挥作用所需的一些部分是:
手动编码后的框架表格元素的简单版本可能如下所示。这将是一个用于创建事件表元素数组的元素,每个事件消息标识符一个。
typedef struct {
int msgId; // the identifier for the message type
void (*handler)(int msgId, void *msgData); // function pointer to handler
} SimpleEventTable;
在没有宏的源代码中使用此功能的不完整示例如下所示。在此示例中,MSG_ID_ONE 和 MSG_ID_TWO 使用 int 值定义,而 handler1 和 handler2 是处理这些消息的函数:
void handler1 (int msgId, void *msgData)
{
// do things with the data associated with message identifier MSG_ID_ONE
}
void handler2 (int msgId, void *msgData)
{
// do things with the data associated with message identifier MSG_ID_TWO
}
SimpleEventTable myTable[] = {
{MSG_ID_ONE, handler1},
{MSG_ID_TWO, handler2},
{0, NULL}
};
然后框架使用事件表来确定代码是否正在处理特定的消息标识符,以及应用程序是否正在处理消息标识符,调用什么函数来处理它。该框架将提供一个默认处理程序,在许多情况下,该处理程序除了指示消息已被处理之外什么都不做。
大多数框架都希望更容易地完成相当于样板源代码的工作,它们将提供一组预处理器宏,使事件表更易于创建。
来自 MFC 的示例宏
我无法访问 wxWidgets,但是 Microsoft MFC 框架提供了与 wxWidgets 框架类似的东西,尽管 MFC 中的内容比窗口管理更多。
使用 MFC 的事件表如下所示,实际上与您使用的框架非常相似。此消息映射是 MFC 窗口类的实现文件的一部分。在这种情况下,应用程序类 CFrameworkWndDoc 派生自 MFC 类 CWindowDocument,它是 MFC 框架的一部分。
BEGIN_MESSAGE_MAP(CFrameworkWndDoc, CWindowDocument)
ON_WM_CHAR()
ON_WM_TIMER()
ON_MESSAGE(WU_EVS_DFLT_LOAD, OnDefaultWinLoad)
ON_MESSAGE(WM_APP_SHOW_HIDE_GROUP, OnShowHideGroupMsgRcvd)
END_MESSAGE_MAP()
除了在实现源文件.c文件中的上述消息映射之外,还有在类定义中使用DECLARE_MESSAGE_MAP()定义,通常位于头文件中,因此有一个类和消息映射之间的联系。
DECLARE_MESSAGE_MAP() 宏只是将必要的声明放入类定义中,将实现文件中的实际消息映射与类联系起来。
#define DECLARE_MESSAGE_MAP() \
protected: \
static const AFX_MSGMAP* PASCAL GetThisMessageMap(); \
virtual const AFX_MSGMAP* GetMessageMap() const; \
MFC 包含文件具有如下定义。首先是事件表的开始和结束的两个定义,或者 MFC 所说的消息映射。如您所见,这为 MFC 类创建了一个新成员,该成员由 MFC 框架调用,以便在 MFC 框架处理消息时访问消息映射。 class 定义中的DECLARE_MESSAGE_MAP() 宏声明了由以下宏生成的函数。
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \
PTM_WARNING_DISABLE \
const AFX_MSGMAP* theClass::GetMessageMap() const \
{ return GetThisMessageMap(); } \
const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \
{ \
typedef theClass ThisClass; \
typedef baseClass TheBaseClass; \
static const AFX_MSGMAP_ENTRY _messageEntries[] = \
{
#define END_MESSAGE_MAP() \
{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \
}; \
static const AFX_MSGMAP messageMap = \
{ &TheBaseClass::GetThisMessageMap, &_messageEntries[0] }; \
return &messageMap; \
} \
PTM_WARNING_RESTORE
然后有一些宏用于简化事件表的构建。 MFC 使用的这些比上面的简单示例要复杂得多,因为它们旨在与 MFC 窗口类一起使用并插入到源代码文件中并由 Visual Studio 开发环境管理。
还要注意,MFC 消息映射技术使用一组特殊标识符,AfxSig_vwww 和 AfxSig_lwl,它们告诉 MFC 框架处理函数的接口应该是什么,(UINT, UINT, UINT) 和 @987654339 @分别。
#define ON_WM_CHAR() \
{ WM_CHAR, 0, 0, 0, AfxSig_vwww, \
(AFX_PMSG)(AFX_PMSGW) \
(static_cast< void (AFX_MSG_CALL CWnd::*)(UINT, UINT, UINT) > ( &ThisClass :: OnChar)) },
#define ON_MESSAGE(message, memberFxn) \
{ message, 0, 0, 0, AfxSig_lwl, \
(AFX_PMSG)(AFX_PMSGW) \
(static_cast< LRESULT (AFX_MSG_CALL CWnd::*)(WPARAM, LPARAM) > \
(memberFxn)) },