MFC 中的消息映射提供了将 Windows 消息定向到适当的C++对象实例的有效方法。 MFC 消息映射目标的示例包括应用程序类、文档类和视图类、控件类等。
传统 MFC 消息映射使用 BEGIN_MESSAGE_MAP 宏声明消息映射的开头、每个消息处理程序类方法的宏条目,最后 声明END_MESSAGE_MAP 宏来声明消息映射的末尾。
当 BEGIN_MESSAGE_MAP 宏与包含模板参数的类结合使用时,将发生BEGIN_MESSAGE_MAP宏的一个限制。 与模板类一起使用时,此宏将导致编译时错误,因为宏扩展期间缺少模板参数。 BEGIN_TEMPLATE_MESSAGE_MAP宏旨在允许包含单个模板参数的类声明其自己的消息映射。
示例:
请考虑一个示例,其中 MFC CListBox 类进行了扩展,以提供与外部数据源的同步。 虚构 CSyncListBox
类声明如下:
// Extends the CListBox class to provide synchronization with
// an external data source
template <typename CollectionT>
class CSyncListBox : public CListBox
{
public:
CSyncListBox();
virtual ~CSyncListBox();
afx_msg void OnPaint();
afx_msg void OnDestroy();
afx_msg LRESULT OnSynchronize(WPARAM wParam, LPARAM lParam);
DECLARE_MESSAGE_MAP()
// ...additional functionality as needed
};
该 CSyncListBox
类在描述要同步的数据源的单个类型上进行模板化。 它还声明将参与类的消息映射的三种方法: OnPaint
、 OnDestroy
和 OnSynchronize
。 该方法 OnSynchronize
的实现方式如下:
template <class CollectionT>
LRESULT CSyncListBox<CollectionT>::OnSynchronize(WPARAM, LPARAM lParam)
{
CollectionT* pCollection = (CollectionT*)(lParam);
ResetContent();
if (pCollection != NULL)
{
INT nCount = (INT)pCollection->GetCount();
for (INT n = 0; n < nCount; n++)
{
CString s = StringizeElement(pCollection, n);
AddString(s);
}
}
return 0L;
}
上述实现允许 CSyncListBox
类专用于实现 GetCount
方法的任何类类型,例如 CArray
, CList
和 CMap
。 该 StringizeElement
函数是由以下模板函数原型生成的:
// Template function for converting an element within a collection
// to a CString object
template<typename CollectionT>
CString StringizeElement(CollectionT* pCollection, INT iIndex);
通常,此类的消息映射将定义为:
BEGIN_MESSAGE_MAP(CSyncListBox, CListBox)
ON_WM_PAINT()
ON_WM_DESTROY()
ON_MESSAGE(LBN_SYNCHRONIZE, OnSynchronize)
END_MESSAGE_MAP()
其中 LBN_SYNCHRONIZE 是由应用程序定义的自定义用户消息,例如:
#define LBN_SYNCHRONIZE (WM_USER + 1)
由于宏扩展期间缺少类的 CSyncListBox
模板规范,上述宏映射不会编译。
BEGIN_TEMPLATE_MESSAGE_MAP宏通过将指定的模板参数合并到展开的宏映射中来解决此问题。 此类的消息映射变为:
BEGIN_TEMPLATE_MESSAGE_MAP(CSyncListBox, CollectionT, CListBox)
ON_WM_PAINT()
ON_WM_DESTROY()
ON_MESSAGE(LBN_SYNCHRONIZE, OnSynchronize)
END_MESSAGE_MAP()
下面演示了使用CStringList
对象的示例类用法CSyncListBox
:
void CSyncListBox_Test(CWnd* pParentWnd)
{
CSyncListBox<CStringList> ctlStringLB;
ctlStringLB.Create(WS_CHILD | WS_VISIBLE | LBS_STANDARD | WS_HSCROLL,
CRect(10, 10, 200, 200), pParentWnd, IDC_MYSYNCLISTBOX);
// Create a CStringList object and add a few strings
CStringList stringList;
stringList.AddTail(_T("A"));
stringList.AddTail(_T("B"));
stringList.AddTail(_T("C"));
// Send a message to the list box control to synchronize its
// contents with the string list
ctlStringLB.SendMessage(LBN_SYNCHRONIZE, 0, (LPARAM)& stringList);
// Verify the contents of the list box by printing out its contents
INT nCount = ctlStringLB.GetCount();
for (INT n = 0; n < nCount; n++)
{
TCHAR szText[256];
ctlStringLB.GetText(n, szText);
TRACE(_T("%s\n"), szText);
}
}
若要完成测试,该 StringizeElement
函数必须专用于处理 CStringList
类:
template<>
CString StringizeElement(CStringList* pStringList, INT iIndex)
{
if (pStringList != NULL && iIndex < pStringList->GetCount())
{
POSITION pos = pStringList->GetHeadPosition();
for (INT i = 0; i < iIndex; i++)
{
pStringList->GetNext(pos);
}
return pStringList->GetAt(pos);
}
return CString(); // or throw, depending on application requirements
}