このノートでは、カスタム コントロールと自己描画コントロールの 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
所有者描画構造体 (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または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 を呼び出すときに作成されます。アプリケーションは、既存の
CWnd
にHWND
をアタッチします。 既存のウィンドウの動作は変更されません。 これは委任の場合であり、 CWnd::Attach を呼び出して、既存のHWND
をCWnd
オブジェクトにエイリアス化することで可能になります。CWnd
は既存のHWND
にアタッチされており、派生クラスの動作を変更できます。 実行時に Windows オブジェクトの動作とクラスを変更するため、これは動的サブクラス化と呼ばれます。
動的サブクラス化は、 メソッド CWnd::サブクラスウィンドウ と CWnd::サブクラスDlgItem を使用して実現できます。
どちらのルーチンも、既存のCWnd
にHWND
オブジェクトをアタッチします。
SubclassWindow
は、 HWND
を直接受け取ります。
SubclassDlgItem
は、コントロール ID と親ウィンドウを受け取るヘルパー関数です。
SubclassDlgItem
は、ダイアログ テンプレートから作成されたダイアログ コントロールに C++ オブジェクトをアタッチするために設計されています。
とSubclassWindow
を使用する場合のいくつかの例については、SubclassDlgItem
の例を参照してください。