次の方法で共有


TN014: カスタム コントロール

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

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

Owner-Draw コントロール/メニュー

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

MFC は、次の関数を使用して所有者描画を直接サポートします。

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

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

Self-Draw コントロールとメニュー

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
    

所有者描画構造体 (DRAWITEMSTRUCTMEASUREITEMSTRUCTCOMPAREITEMSTRUCT、およびDELETEITEMSTRUCT) の詳細については、それぞれ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またはCBS_SORT スタイルを持つ自己描画リスト ボックスとコンボ ボックスの場合は、 OnCompareItem メソッドをオーバーライドする必要があります。

自己描画リスト ボックスとコンボ ボックスの場合、 OnDeleteItem は通常オーバーライドされません。 特別な処理を実行する場合は、 OnDeleteItem をオーバーライドできます。 これが該当する 1 つのケースは、追加のメモリまたは他のリソースが各リスト ボックスまたはコンボ ボックス項目と共に格納される場合です。

Self-Drawing コントロールとメニューの例

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

自己描画ボタンの最も一般的な例は、ビットマップ ボタンです。 ビットマップ ボタンは、さまざまな状態の 1 つ、2 つ、または 3 つのビットマップ 画像を示すボタンです。 この例は、MFC クラス CBitmapButton で提供されています。

動的サブクラス化

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

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

これは、C++ クラスの派生と混同しないでください。 明確にするために、C++ 用語の 基底クラス派生クラス は、Windows オブジェクト モデルの スーパークラスサブクラス に似ています。 MFC および Windows サブクラス化を使用した C++ 派生は機能的に似ていますが、C++ では動的サブクラス化がサポートされていません。

CWnd クラスは、(CWnd から派生した) C++ オブジェクトと Windows ウィンドウ オブジェクト (HWND と呼ばれます) の間の接続を提供します。

これらが関連する 3 つの一般的な方法があります。

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

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

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

動的サブクラス化は、 メソッド CWnd::サブクラスウィンドウCWnd::サブクラスDlgItem を使用して実現できます。

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

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

こちらも参照ください

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