MFC 中的訊息對應提供有效率的方式,將 Windows 訊息導向適當的C++對象實例。 MFC 訊息對應目標的範例包括應用程式類別、檔和檢視類別、控件類別等等。
傳統的 MFC 訊息映射使用 BEGIN_MESSAGE_MAP 巨集宣告訊息映射的開始,為每個訊息處理類別方法建立宏條目,最後使用 END_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()
下列示範如何使用 CSyncListBox 類別搭配 CStringList 物件:
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
}