次の方法で共有


テクニカル ノート 14: カスタム コントロール

ここでは、MFC でサポートされているカスタム コントロールと自己描画コントロールについて説明します。 さらに、動的なサブクラス化について取り上げ、CWnd オブジェクトと HWND の関係について説明します。

MFC サンプル アプリケーション CTRLTEST によって、多くのカスタム コントロールの使用方法を学習できます。 MFC の「標準のサンプル」の CTRLTEST のソース コードとオンライン ドキュメントを参照してください。

オーナー描画コントロール/メニュー

Windows では、Windows メッセージを使用したオーナー描画のコントロールとメニューがサポートされています。 コントロールまたはメニューの親ウィンドウは、これらのメッセージを受け取り、応答として関数を呼び出します。 これらの関数をオーバーライドして、オーナー描画のコントロールまたはメニューの外観と動作をカスタマイズできます。

MFC は、次の関数によるオーナー描画を直接サポートしています。

CWnd 派生クラスにあるこれらの関数をオーバーライドして、カスタム描画動作を実装できます。

しかし、この方法ではコードを再利用できません。 よく似た 2 つのコントロールが別々の CWnd クラスにある場合は、両方の場所でカスタム コントロール動作を実装する必要があります。 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
    

オーナー描画構造体 (DRAWITEMSTRUCTMEASUREITEMSTRUCTCOMPAREITEMSTRUCTDELETEITEMSTRUCT) の詳細については、MFC のドキュメントで CWnd::OnDrawItemCWnd::OnMeasureItemCWnd::OnCompareItemCWnd::OnDeleteItem を参照してください。

自己描画のコントロールおよびメニューの使用

自己描画メニューの場合は、メソッド OnMeasureItemOnDrawItem の両方をオーバーライドします。

自己描画のリスト ボックスとコンボ ボックスの場合は、OnMeasureItemOnDrawItem をオーバーライドします。 ダイアログ テンプレートで、リスト ボックスの場合は LBS_OWNERDRAWVARIABLE スタイルを指定し、コンボ ボックスの場合は CBS_OWNERDRAWVARIABLE スタイルを指定します。 OWNERDRAWFIXED スタイルを自己描画アイテムに使用できないのは、アイテムの固定高を決めてから、自己描画コントロールをリスト ボックスに結び付けるためです。 この制限に対処するには、CListBox::SetItemHeight メソッドと CComboBox::SetItemHeight メソッドを使用します。

OWNERDRAWVARIABLE スタイルに切り替えると、コントロールに NOINTEGRALHEIGHT スタイルが強制的に適用されます。 コントロールは可変サイズ アイテムの高さの合計を計算できないので、INTEGRALHEIGHT の既定のスタイルは無視され、コントロールは必ず NOINTEGRALHEIGHT になります。 固定高のアイテムの場合は、コントロールのサイズをアイテム サイズの整数倍と指定しておけば、アイテムが一部しか描画されないということは起こりません。

LBS_SORT スタイルまたは CBS_SORT スタイルの自己描画リスト ボックスとコンボ ボックスの場合は、OnCompareItem メソッドをオーバーライドします。

自己描画リスト ボックスとコンボ ボックスの場合は通常、OnDeleteItem をオーバーライドしません。 ただし、特別な処理を実行する場合は、OnDeleteItem をオーバーライドできます。 たとえば、それぞれのリスト ボックス アイテムやコンボ ボックス アイテムで追加のメモリや他のリソースを格納する場合などが考えられます。

自己描画のコントロールおよびメニューの例

MFC の「標準のサンプル」の CTRLTEST には、自己描画メニューと自己描画リスト ボックスのサンプルが用意されています。

自己描画ボタンの最も典型的な例は、ビットマップ ボタンです。 ビットマップ ボタンとは、それぞれの状態に応じて、1 つ、2 つ、または 3 つのビットマップ イメージを表示するボタンです。 その例が MFC クラス CBitmapButton にあります。

動的なサブクラス化

場合によっては、既存のオブジェクトの機能を変更できると便利です。 これまでに取り上げた例では、コントロールを作成する前にカスタマイズする必要がありました。 動的なサブクラス化という機能を使用すると、作成済みのコントロールをカスタマイズできます。

サブクラス化とは Windows 用語であり、ウィンドウの WndProc をカスタマイズされた WndProc に置き換え、既定の機能については WndProc を呼び出すことです。

この機能は、C++ のクラス派生とは違います。 C++ の用語である基本クラスと派生クラスは、Windows オブジェクト モデルのスーパークラスとサブクラスに似ています。 MFC における C++ の派生と Windows のサブクラス化は機能的に似ていますが、C++ では動的なサブクラス化をサポートしていません。

CWnd クラスによって、C++ のオブジェクト (CWnd からの派生) と Windows のウィンドウ オブジェクト (つまり HWND) 間が接続されます。

CWndHWND の間は通常以下の 3 とおりの関係があります。

  • CWndHWND を作成します。 CWnd から派生したクラスを作成することによって、派生クラスの動作を変更できます。 アプリケーションから CWnd::Create を呼び出すと、HWND が作成されます。

  • アプリケーションは、CWnd を既存の HWND に結び付けます。 既存ウィンドウの動作は変わりません。 これは処理の代行の一例であり、この場合は、CWnd::Attach を呼び出して既存の HWNDCWnd オブジェクトのエイリアスにします。

  • CWnd を既存の HWND に結び付けると、派生クラスの動作を変更できます。 この手法が動的なサブクラス化と呼ばれるのは、実行時に Windows オブジェクトの動作とクラスを変更するためです。

動的なサブクラス化を実装するには、CWnd::SubclassWindow メソッドとCWnd::SubclassDlgItem メソッドを使用します。

この 2 つのルーチンは CWnd オブジェクトを既存の HWND に結び付けます。 SubclassWindowHWND を直接受け取ります。 SubclassDlgItem はコントロール ID と親ウィンドウを使用するヘルパー関数です。 SubclassDlgItem は、C++ オブジェクトをダイアログ テンプレートから作成したダイアログ コントロールに結び付けます。

SubclassWindowSubclassDlgItem の使用例については、サンプルの「CTRLTEST サンプル : カスタム コントロールの実装」を参照してください。

参照

その他の技術情報

番号順テクニカル ノート

カテゴリ別テクニカル ノート