如何:建立樣板類別的訊息對應
在 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;
}
上述實作在執行 GetCount 方法,例如 CArray, CList的任何類別型別 CMap和 CSyncListBox 允許類別特製化。 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
}