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

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

MFC サンプル アプリケーション CTRLTEST は、多くのカスタム コントロールを使用する方法を示しています。 MFC の一般的なサンプル CTRLTEST とオンライン ヘルプのソース コードを参照してください。

所有者描画コントロール/メニュー

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

MFC では、次の関数を使用した所有者描画が直接サポートされます:

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

この方法では、再利用可能なコードは得られません。 2 つの異なる CWnd クラスに 2 つの類似したコントロールがある場合は、2 つの場所にカスタム コントロールの動作を実装する必要があります。 MFC でサポートされている自己描画コントロール アーキテクチャによって、この問題が解決されます。

自己描画コントロールとメニュー

MFC は、標準の所有者描画メッセージの既定の実装 ( CWndCMenu クラス) を提供します。 この既定の実装では、所有者描画パラメーターがデコードされ、所有者描画メッセージがコントロールまたはメニューに委任されます。 描画コードはコントロールまたはメニューの クラスに含まれ、所有者ウィンドウには含めないため、これは自己描画と呼ばれます。

自己描画コントロールを使用すると、所有者描画セマンティクスを使用してコントロールを表示する再利用可能なコントロール クラスを構築できます。 コントロールを描画するコードは、その親ではなく、コントロール クラス内に配置されます。 これは、カスタム コントロール プログラミングに対するオブジェクト指向のアプローチです。 次の関数の一覧を自己描画クラスに追加します:

  • 自己描画ボタンの場合:

    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) の詳細については、CWnd::OnDrawItemCWnd::OnMeasureItemCWnd::OnCompareItem、および CWnd::OnDeleteItem の MFC ドキュメントをそれぞれ参照してください。

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

自己描画メニューの場合は、 OnMeasureItem メソッドと OnDrawItem メソッドの両方をオーバーライドする必要があります。

自己描画リスト ボックスとコンボ ボックスの場合は、OnMeasureItemOnDrawItem をオーバーライドする必要 があります。 ダイアログ テンプレートのリスト ボックスには、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 種類あります:

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

  • アプリケーションは、CWnd を既存のHWND にアタッチします。 既存のウィンドウの動作は変更されません。 これは委任のケースであり、CWnd::Attach を呼び出して、既存の HWNDCWnd オブジェクトにエイリアスとして指定することで可能になります。

  • CWnd は既存の HWND にアタッチされ 、派生クラスの動作を変更できます。 これが動的サブクラス化と呼ばれるのは、実行時に Windows オブジェクトの動作 (したがって、クラス) 変更するためです。

動的サブクラス化は、CWnd::SubclassWindowCWnd::SubclassDlgItem のメソッド使用して実現できます。

どちらのルーチンでも、CWnd オブジェクトを 既存の HWND にアタッチします。 SubclassWindowHWND を直接受け取ります。 SubclassDlgItem は、コントロール ID と親ウィンドウを受け取るヘルパー関数です。 SubclassDlgItem は、ダイアログ テンプレートから作成されたダイアログ コントロールに C++ オブジェクトをアタッチするように設計されています。

SubclassWindowSubclassDlgItem を使用する場合のいくつかの例については、CTRLTEST の例を参照してください。

関連項目

番号順テクニカル ノート
カテゴリ別テクニカル ノート