テクニカル ノート 14: カスタム コントロール
このノートでは、カスタム コントロールと自己描画コントロールの MFC サポートについて説明します。 また、動的サブクラス化について説明し、CWnd オブジェクトと HWND
の関係について説明します。
MFC サンプル アプリケーション CTRLTEST は、多くのカスタム コントロールを使用する方法を示しています。 MFC の一般的なサンプル CTRLTEST とオンライン ヘルプのソース コードを参照してください。
所有者描画コントロール/メニュー
Windows メッセージを使用して、所有者描画コントロールとメニューをサポートします。 コントロールまたはメニューの親ウィンドウは、これらのメッセージを受信し、応答で関数を呼び出します。 これらの関数をオーバーライドして、所有者描画コントロールまたはメニューの外観と動作をカスタマイズできます。
MFC では、次の関数を使用した所有者描画が直接サポートされます:
CWnd
派生クラスでこれらの関数をオーバーライドして、カスタム描画動作を実装できます。
この方法では、再利用可能なコードは得られません。 2 つの異なる CWnd
クラスに 2 つの類似したコントロールがある場合は、2 つの場所にカスタム コントロールの動作を実装する必要があります。 MFC でサポートされている自己描画コントロール アーキテクチャによって、この問題が解決されます。
自己描画コントロールとメニュー
MFC は、標準の所有者描画メッセージの既定の実装 ( CWnd
と CMenu クラス) を提供します。 この既定の実装では、所有者描画パラメーターがデコードされ、所有者描画メッセージがコントロールまたはメニューに委任されます。 描画コードはコントロールまたはメニューの クラスに含まれ、所有者ウィンドウには含めないため、これは自己描画と呼ばれます。
自己描画コントロールを使用すると、所有者描画セマンティクスを使用してコントロールを表示する再利用可能なコントロール クラスを構築できます。 コントロールを描画するコードは、その親ではなく、コントロール クラス内に配置されます。 これは、カスタム コントロール プログラミングに対するオブジェクト指向のアプローチです。 次の関数の一覧を自己描画クラスに追加します:
自己描画ボタンの場合:
CButton:DrawItem(LPDRAWITEMSTRUCT); // insert code to draw this button
自己描画メニューの場合:
CMenu:MeasureItem(LPMEASUREITEMSTRUCT); // insert code to measure the size of an item in this menu CMenu:DrawItem(LPDRAWITEMSTRUCT); // insert code to draw an item in this menu
自己描画リスト ボックスの場合:
CListBox:MeasureItem(LPMEASUREITEMSTRUCT); // insert code to measure the size of an item in this list box CListBox:DrawItem(LPDRAWITEMSTRUCT); // insert code to draw an item in this list box CListBox:CompareItem(LPCOMPAREITEMSTRUCT); // insert code to compare two items in this list box if LBS_SORT CListBox:DeleteItem(LPDELETEITEMSTRUCT); // insert code to delete an item from this list box
自己描画コンボ ボックスの場合:
CComboBox:MeasureItem(LPMEASUREITEMSTRUCT); // insert code to measure the size of an item in this combo box CComboBox:DrawItem(LPDRAWITEMSTRUCT); // insert code to draw an item in this combo box CComboBox:CompareItem(LPCOMPAREITEMSTRUCT); // insert code to compare two items in this combo box if CBS_SORT CComboBox:DeleteItem(LPDELETEITEMSTRUCT); // insert code to delete an item from this combo box
所有者描画構造体 (DRAWITEMSTRUCT、MEASUREITEMSTRUCT、COMPAREITEMSTRUCT、DELETEITEMSTRUCT) の詳細については、CWnd::OnDrawItem
、CWnd::OnMeasureItem
、CWnd::OnCompareItem
、および CWnd::OnDeleteItem
の MFC ドキュメントをそれぞれ参照してください。
自己描画コントロールとメニューの使用
自己描画メニューの場合は、 OnMeasureItem
メソッドと OnDrawItem
メソッドの両方をオーバーライドする必要があります。
自己描画リスト ボックスとコンボ ボックスの場合は、OnMeasureItem
と OnDrawItem
をオーバーライドする必要 があります。 ダイアログ テンプレートのリスト ボックスには、LBS_OWNERDRAWVARIABLE のスタイルを指定するか、コンボ ボックスには CBS_OWNERDRAWVARIABLE スタイルを指定する必要があります。 OWNERDRAWFIXED スタイルは、リスト ボックスに自己描画コントロールがアタッチされる前に固定項目の高さが決定されるので、自己描画項目では機能しません。 (CListBox::SetItemHeight メソッドと CComboBox::SetItemHeight メソッドを使用して、この制限を克服できます)
OWNERDRAWVARIABLE スタイルに切り替えると、コントロールに NOINTEGRALHEIGHT スタイルが強制的に適用されます。 コントロールは可変サイズの項目で整数の高さを計算できないので、INTEGRALHEIGHT の既定のスタイルは無視され、コントロールは常に NOINTEGRALHEIGHT です。 項目の高さが固定されている場合は、項目サイズの整数乗数としてコントロール サイズを指定すると、部分的な項目が描画されるのを防ぐことができます。
LBS_SORT or CBS_SORT スタイルを持つ自己描画リスト ボックスおよびコンボ ボックスの場合、OnCompareItem
メソッドをオーバーライドする必要があります。
自己描画リスト ボックスとコンボ ボックスの場合、 OnDeleteItem
は通常オーバーライドされません。 特別な処理を実行する場合は、 OnDeleteItem
をオーバーライドできます。 これが該当するケースの 1 つは、追加のメモリまたは他のリソースが各リスト ボックスまたはコンボ ボックス項目と一緒に格納されている場合です。
自己描画コントロールとメニューの例
MFC の一般的なサンプル CTRLTEST には、自己描画メニューと自己描画リスト ボックスのサンプルが含まれています。
自己描画ボタンの最も一般的な例は、ビットマップ ボタンです。 ビットマップ ボタンは、異なる状態のビットマップ イメージを 1 つ、2 つ、または 3 つ表示するボタンです。 この例は、MFC クラス CBitmapButton で提供されています。
動的なサブクラス化
場合によっては、既に存在するオブジェクトの機能を変更する必要があります。 前の例では、コントロールを作成する前にカスタマイズする必要があります。 動的サブクラス化を使用すると、既に作成されているコントロールをカスタマイズできます。
サブクラス化は、ウィンドウの WndProc をカスタマイズした WndProc
に置き換え、既定の機能のために古い WndProc
を呼び出す場合の用語です。
これを、C++ クラスの派生と混同しないでください。 明確にするために、C++ 用語の基本クラスと派生クラスは、Windows オブジェクト モデルのスーパークラスとサブクラスに類似しています。 MFC を使用した C++ 派生とWindowsサブクラス化は機能的に似ていますが、C++ では動的サブクラス化がサポートされていません。
CWnd
クラスは、C++ オブジェクト (CWnd
から派生) と Windows ウィンドウ オブジェクト (HWND
と呼ばれる) 間の接続を提供します。
これらが関連する一般的な方法は 3 種類あります:
CWnd
HWND
を作成します。CWnd
から派生したクラスを作成することで、派生クラスの動作を変更できます。HWND
は 、アプリケーションにより CWnd::Create が呼び出された場合に作成されます。アプリケーションは、
CWnd
を既存のHWND
にアタッチします。 既存のウィンドウの動作は変更されません。 これは委任のケースであり、CWnd::Attach を呼び出して、既存のHWND
をCWnd
オブジェクトにエイリアスとして指定することで可能になります。CWnd
は既存のHWND
にアタッチされ 、派生クラスの動作を変更できます。 これが動的サブクラス化と呼ばれるのは、実行時に Windows オブジェクトの動作 (したがって、クラス) 変更するためです。
動的サブクラス化は、CWnd::SubclassWindow とCWnd::SubclassDlgItem のメソッド使用して実現できます。
どちらのルーチンでも、CWnd
オブジェクトを 既存の HWND
にアタッチします。 SubclassWindow
は HWND
を直接受け取ります。 SubclassDlgItem
は、コントロール ID と親ウィンドウを受け取るヘルパー関数です。 SubclassDlgItem
は、ダイアログ テンプレートから作成されたダイアログ コントロールに C++ オブジェクトをアタッチするように設計されています。
SubclassWindow
と SubclassDlgItem
を使用する場合のいくつかの例については、CTRLTEST の例を参照してください。