方法 : テンプレート クラスのメッセージ マップを作成する
MFC メッセージ マップは、適切な C++ オブジェクト インスタンスにウィンドウ メッセージを指示する最も効率的を提供します。MFC メッセージ マップ先の例は、アプリケーションのクラス、ドキュメントとビュー クラスのコントロール クラスが、などがあります。
従来の MFC メッセージ マップは BEGIN_MESSAGE_MAP マクロを使用してメッセージ マップの開始要素、各メッセージハンドラーのクラス メソッドのマクロ エントリ、最後に END_MESSAGE_MAP のマクロを宣言するようにメッセージ マップの終了位置を宣言するように宣言されます。
BEGIN_MESSAGE_MAP マクロの 1 種類の制約を含むクラスのテンプレート引数とともに使用された場合に発生します。テンプレート クラスを使用すると、このマクロは、マクロの配置時にそのテンプレート パラメーターによってコンパイル時のエラーが発生します。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 のクラスは、同期するデータ ソースを記述する一つの型でテンプレート。また、クラスのメッセージ マップに参加する 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;
}
上記の実装は 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
}