次の方法で共有


方法: テンプレート クラスのメッセージ マップを作成する

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 クラスは、同期するデータ ソースを記述する 1 つの型でテンプレート化されます。 また、クラスのメッセージ マップに参加する 3 つのメソッド ( OnPaintOnDestroyOnSynchronize) も宣言します。 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 クラスは、CArrayCListCMapなど、GetCount メソッドを実装する任意のクラス型に特化できます。 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
}

こちらも参照ください

BEGIN_TEMPLATE_MESSAGE_MAP
メッセージの処理とマッピング