コントロールのサブクラス化

コントロールが必要なほとんどすべてを実行するが、さらにいくつかの機能が必要な場合は、元のコントロールをサブクラス化することで、元のコントロールを変更または追加できます。 サブクラスには、既存のクラスのすべての機能と、それを提供する追加の機能を含めることができます。

このドキュメントでは、サブクラスの作成方法について説明し、次のトピックを含めます。

ComCtl32.dll バージョン 6 より前のコントロールのサブクラス化

サブクラスにコントロールを配置し、コントロール内にユーザー データを格納できます。 これは、バージョン 6 より前のバージョンの ComCtl32.dll を使用する場合に行います。 以前のバージョンの ComCtl32.dll でサブクラスを作成する場合には、いくつかの欠点があります。

新しいコントロールを作成するには、Windows の一般的なコントロールの 1 つから始めて、特定のニーズに合わせて拡張することをお勧めします。 コントロールを拡張するには、コントロールを作成し、既存のウィンドウ プロシージャを新しいウィンドウ プロシージャに置き換えます。 新しいプロシージャは、コントロールのメッセージを受信し、それらに対して動作するか、既定の処理のために元のプロシージャに渡します。 SetWindowLong または SetWindowLongPtr 関数を使用して、コントロールの WNDPROC を置き換えます。 次のコード サンプルは、WNDPROC を置き換える方法を示しています。

OldWndProc = (WNDPROC)SetWindowLongPtr (hButton,
GWLP_WNDPROC, (LONG_PTR)NewWndProc);

ユーザー データの格納

個々のウィンドウにユーザー データを格納したい場合があります。 このデータは、コントロールを描画する方法や特定のメッセージを送信する場所を決定するために、新しいウィンドウ プロシージャで使用できます。 たとえば、データを使用して、コントロールを表すクラスへの C++ クラス ポインターを格納できます。 次のコード サンプルは、SetProp を使用してウィンドウにデータを格納する方法を示しています。

SetProp (hwnd, TEXT("MyData"), (HANDLE)pMyData);

古いサブクラス化アプローチの欠点

次の一覧では、前に説明した方法を使用してコントロールをサブクラス化する場合の欠点をいくつか示します。

  • ウィンドウ プロシージャは 1 回だけ置き換えることができます。
  • サブクラスを作成した後で削除することは困難です。
  • プライベート データをウィンドウに関連付けることは非効率的です。
  • サブクラス チェーンで次のプロシージャを呼び出すには、古いウィンドウ プロシージャをキャストして呼び出すことはできません。CallWindowProc 関数を使用して呼び出す必要があります。

ComCtl32.dll バージョン 6 を使用したコントロールのサブクラス化

Note

ComCtl32.dll バージョン 6 は Unicode のみです。 ComCtl32.dll バージョン 6 でサポートされる共通コントロールは、ANSI ウィンドウ プロシージャを使用してサブクラス化 (またはスーパークラス化) しないでください。

 

ComCtl32.dll バージョン 6 には、サブクラスの作成を容易にし、前述の欠点を排除する 4 つの関数が含まれています。 新しい関数は、複数の参照データ セットに関連する管理をカプセル化するため、開発者はサブクラスの管理ではなく、プログラミング機能に集中できます。 サブクラス関数は次のとおりです。

SetWindowSubclass

この関数は、最初にウィンドウをサブクラス化するために使用されます。 各サブクラスは、pfnSubclass とその uIdSubclass のアドレスによって一意に識別されます。 これらはどちらも SetWindowSubclass 関数のパラメーターです。 複数のサブクラスが同じサブクラス プロシージャを共有でき、ID は各呼び出しを識別できます。 参照データを変更するには、後で SetWindowSubclass を呼び出します。 重要な利点として、各サブクラス インスタンスに独自の参照データがある点が挙げられます。

サブクラス プロシージャの宣言は、サブクラス ID と参照データという 2 つの追加データがあるため、通常のウィンドウ プロシージャとは若干異なります。 次の関数宣言の最後の 2 つのパラメーターは、これを示しています。

LRESULT CALLBACK MyWndProc (HWND hWnd, UINT msg,
WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass,
DWORD_PTR dwRefData);

新しいウィンドウ プロシージャによってメッセージが受信されるたびに、サブクラス ID と参照データが含まれます。

Note

プロシージャに渡されるすべての文字列は、Unicode がプリプロセッサ定義として指定されていない場合でも Unicode 文字列です。

 

次の例は、サブクラス化されたコントロールのウィンドウ プロシージャのスケルトン実装を示しています。

LRESULT CALLBACK OwnerDrawButtonProc(HWND hWnd, UINT uMsg, WPARAM wParam,
                               LPARAM lParam, UINT_PTR uIdSubclass, DWORD_PTR dwRefData)
{
    switch (uMsg)
    {
    case WM_PAINT:
        .
        .
        .
        return TRUE;
    // Other cases...
    } 
    return DefSubclassProc(hWnd, uMsg, wParam, lParam);
}

ウィンドウ プロシージャは、次の例に示すように、ダイアログ プロシージャの WM_INITDIALOG ハンドラーのコントロールにアタッチできます。

case WM_INITDIALOG:
{
    HWND button = GetDlgItem(hDlg, IDC_OWNERDRAWBUTTON);
    SetWindowSubclass(button, OwnerDrawButtonProc, 0, 0);
    return TRUE;
}

GetWindowSubclass

この関数は、サブクラスに関する情報を取得します。 たとえば、GetWindowSubclass を使用すると、参照データにアクセスできます。

RemoveWindowSubclass

この関数はサブクラスを削除します。 RemoveWindowSubclassSetWindowSubclass を組み合わせることで、サブクラスを動的に追加および削除できます。

DefSubclassProc

DefSubclassProc 関数は、サブクラス チェーン内の次のハンドラーを呼び出します。 この関数は、適切な ID と参照データも取得し、次のウィンドウ プロシージャに情報を渡します。