方法 : テンプレート クラスのメッセージ マップを作成する
MFC のメッセージ マッピングは、Windows メッセージを適切な C++ オブジェクト インスタンスに転送する効率的な方法を提供します。 MFC メッセージ マップのターゲットの例としては、アプリケーション クラス、ドキュメントとビュー クラス、コントロール クラスなどがあります。
従来の MFC メッセージ マップは、メッセージ マップの先頭を宣言する BEGIN_MESSAGE_MAP マクロ、各メッセージハンドラー クラス メソッドのマクロ エントリ、最後にメッセージ マップの末尾を宣言する END_MESSAGE_MAP マクロを使用して宣言します。
BEGIN_MESSAGE_MAP マクロの 1 つの制限は、テンプレート引数を含むクラスと組み合わせて使用した場合に発生します。 テンプレート クラスと共に使用した場合、マクロの展開中にテンプレート パラメーターが見つからないことが原因で、このマクロではコンパイル時エラーが発生します。 BEGIN_TEMPLATE_MESSAGE_MAP マクロは、1 つのテンプレート引数を含むクラスがそれ自体のメッセージ マップを宣言できるように設計されています。
例
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
クラスは、同期されるデータ ソースを記述した単一の型で template 宣言されています。 また、クラスのメッセージ マップに参加する 3 つのメソッド (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;
}
上記の実装では、CArray
、CList
、CMap
など、GetCount
メソッドを実装する任意のクラス型に 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);
}
}
テストを完了するには、CStringList
クラスを使用するように StringizeElement
関数を特殊化する必要があります。
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
}