マネージ ウィンドウ プロシージャを使用したコントロールのサブクラス化

更新 : 2007 年 11 月

.NET Compact Framework には、コールバックのデリゲートを使用してネイティブ コードからマネージ コードを呼び出す機能があります。マネージ コントロールをサブクラス化して、対応するネイティブ コードからコールバックを受け取ることで、.NET Compact Framework では直接使用できない機能を持つコントロールを作成できます。

これは、Windows プログラミングとコントロールのサブクラス化に関する知識を持つ開発者向けの応用的なトピックです。コントロールをサブクラス化するには、ネイティブ コントロールの内部、およびマネージ コントロールの拡張で実装する機能に割り当てる方法に関する詳細な知識が必要です。目的の機能を実現するために、どの Windows メッセージを監視し、どのネイティブ Windows Embedded CE 関数を呼び出すかについても理解する必要があります。

ここでは、TreeView コントロールと Button コントロールのサブクラス化について説明します。以降のトピックでは、コード例とアプリケーションのビルド手順について説明します。

方法のトピック

説明内容

方法 : ネイティブのコールバックを使用して TreeView をサブクラス化する

TreeView コントロールをサブクラス化して、NodeMouseClick イベントの実装を作成する方法。.NET Compact Framework では、リソースが限られているためサイズの制限が必要であることを理由に、このメソッドを直接サポートしていません。

方法 : ネイティブのコールバックを使用してボタンをサブクラス化する

Button コントロールをサブクラス化して、カラフルなグラデーションの塗りつぶしを表示する方法。

このボタンは、主にサブクラス化とコールバックを使用する方法を説明するために用意されていることに注意してください。グラデーションの塗りつぶしが施されたボタンを作成する簡単な方法については、「方法 : グラデーションの塗りつぶしを表示する」を参照してください。

どちらのサブクラス化プログラミングにも、ネイティブ Win32 構造体の WndProcHooker クラスとヘルパー クラス、プラットフォーム呼び出しの宣言、および WndProc デリゲートが含まれます。コードの一覧については、「方法 : Windows プロシージャをフックするためのクラスを使用する」および「方法 : プラットフォーム呼び出しにヘルパー クラスを使用する」を参照してください。

WndProcHooker クラス

WndProcHooker クラスには、ネイティブ コントロールまたはネイティブ ウィンドウで、特定の Windows メッセージを受け取ったときに、マネージ コードへのコールバックを呼び出す機能があります。これを実行するには、ネイティブ コントロールのウィンドウ プロシージャ (WndProc) を標準のウィンドウ プロシージャ WindowProc (コントロールが、呼び出すコールバック メソッドに関連付けられたコントロール リストに含まれるかどうかを判断するために実行されます) で置き換えます。この場合、コントロールはフックされたと見なされます。

コントロールがフックされると、WindowProc は、特定の Windows メッセージに応答するコントロールが割り当てられているかどうかを判断します。これはメッセージ マップです。メッセージ マップは、Windows メッセージを WndProcCallback デリゲートに割り当てます。このデリゲートで、目的の機能を持つマネージ コードが呼び出されます。メッセージ マップにメッセージが含まれる場合、WindowProc に指定されたメッセージ パラメータを使用して、WndProcCallback デリゲートからコードが呼び出されます。

コントロールのフック

HookWndProc メソッドは、コントロールのハンドルを、標準のウィンドウ プロシージャ WindowProc で使用されるメッセージ マップに関連付けます。これはコントロールのフックと呼ばれます。

HookWndProc メソッドでは、コントロールがフック済みかどうかを判断します。フックされていない場合、そのコントロールの HookedProcInformation オブジェクトが作成されます。このオブジェクトには、コントロールとメッセージ マップへの参照が含まれます。コントロールのハンドルが作成済みの場合、後で復元できるように、ウィンドウの元のウィンドウ プロシージャへのポインタを作成してウィンドウをフックします。ハンドルが作成されていない場合、HandleCreated イベントを処理する ctrl_HandleCreated メソッドでフックされます。

次に、HookWndProc メソッドで、2 つの標準のディレクトリ コレクションのいずれかに HookedProcInformation オブジェクトを追加します。

  • hwindDict ディクショナリには、フック済みのウィンドウ ハンドルのすべてのグローバル リストが含まれます。キーは hwnd です。ハンドルが作成済みのコントロールは、このディクショナリに登録されます。このディクショナリのコントロールは、すべての割り当てられたメッセージの WindowProc で評価されます。

  • ctlDict ディクショナリ。ハンドルが作成されていないコントロールが含まれます。ctrl_HandleCreated メソッドが呼び出されると、コントロールは hwndDict ディクショナリに移動されます。

コントロールのアンフック

UnhookWndProc メソッドには、コントロールをアンフックする方法が 2 つあります。

  • コントロールのメッセージ マップからメッセージを削除しますが、フックされたウィンドウの hwndDict ディクショナリにはコントロールを残します。この方法では、HookedProcInformation オブジェクトに保持されているポインタを使用して、コントロールの元のウィンドウ プロシージャを復元します。

  • フックされたコントロールの hwndDict ディクショナリからコントロールを削除します。また、そのコントロールのハンドルを削除して ctrlDict ディクショナリに配置するか、コントロール全体を破棄します。さらに、この方法では、HookedProcInformation オブジェクトに保持されているハンドルを使用して、コントロールの元のウィンドウ プロシージャを復元します。

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

方法 : ネイティブのコールバックを使用して TreeView をサブクラス化する」で紹介されているサンプル プログラム TreeViewBonus クラスでは、TreeView コントロールを拡張し、NodeMouseClick イベントを含めています。このイベントは .NET Compact Framework からは直接使用できません。

WndProcHooker クラスと同様に、NodeMouseClick イベントを取得するには、コントロールのメッセージ マップに WM_NOTIFY メッセージを追加します。マネージ コールバック メソッド WM_Notify_Handler では、Windows メッセージが送信されたときに、ネイティブの GetMessagePos 関数を呼び出して、マウス カーソルの座標を取得します。

この座標は、画面に表示されているクライアント領域に対する相対座標であることに注意してください。TreeView コントロールに対する相対座標ではありません。TreeViewBonus クラスでは、コントロールの PointToClient メソッドを使用して、画面座標をクライアント座標に変換します。このクライアント座標は、TreeViewBonus オブジェクトがクリックされたかどうかおよびクリック場所を示す TVM_HITTEST メッセージと共に送信されます。

ネイティブ コントロールの TVM_HITTEST メッセージを使用して、TreeViewBonus クラスには、コントロールに対する相対座標を取得するコードが含まれます。

ツリー ビュー ノードのいずれかでクリックが発生すると、ネイティブの TVHITTESTINFO 構造体にはそのノードのハンドルが含まれます。最後の手順として、マネージ TreeView ノードを反復的に走査 (FindTreeNodeFromHandle メソッドで実行されます) して、一致するハンドルを検索し、NodeMouseClick イベントを発生させます。TreeNodeMouseClickEventArgs クラスには、以下のデータがあります。

  • クリックされたノード

  • クリックされたボタン

  • クリック回数 (1 に設定されています)

  • クリックが発生した X 座標

  • クリックが発生した Y 座標

TreeViewBonus クラスは、WndProcHooker クラスと同様に、ネイティブのツリー ビュー コントロールの親をマネージ ウィンドウ プロシージャにフックします。親コントロールをフックすることで、OnParentChanged イベントに応答します。そのため、TreeView が新しい親に移動した場合 (Form からの移動、Panel への移動など) にも対応できます。

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

方法 : ネイティブのコールバックを使用してボタンをサブクラス化する」で紹介されている GradientFilledButton クラスと GradientFill クラスでは、Button コントロールを拡張し、2 色間のグラデーションの塗りつぶしを表示しています。このプログラムは、主にサブクラス化を説明する目的で作成されています。ボタンの塗りつぶしをグラデーションにするには、Control から派生したカスタム コントロールを作成するという、より簡単な方法があります。「方法 : グラデーションの塗りつぶしを表示する」を参照してください。

GradientFilledButton クラスのコンストラクタによって、WndProcHooker クラスのインスタンスを作成し、Windows メッセージをマネージ コールバックに割り当てます。このコールバック メソッドは、Windows メッセージおよびコントロールの Capture プロパティに応じた適切な状態のボタンを描画します。Windows メッセージの割り当てと、対応するコールバックを次の表に示します。

Windows メッセージ

マネージ コールバック メソッドと説明

WM_KEYDOWN

WM_KeyDown_Handler - Space キーまたは Enter (または Action) キーが押されたときに、プッシュ状態のボタンが再描画されます。

WM_KEYUP

WM_KeyUp_Handler - 押されたキーが Space キーまたは Enter (Action) キーの場合、アンプッシュ状態のボタンが再描画され、Click イベントが発生します。

WM_LBUTTONDOWN

WM_LeftButtonDown_Hander - プッシュ状態のボタンが再描画され、そのコントロールでマウスの Capture プロパティが true に設定されます。

WM_LBUTTONUP

WM_LButtonUp_Handler - アンプッシュ状態のボタンが再描画され、カーソルがコントロールのクライアント領域で離された場合は MouseUp イベントが発生し、そのコントロールでマウスの Capture プロパティが false に設定されます。

WM_MOUSEMOVE

WM_MouseMove_Handler - ボタンが以前にクリックされているときはボタンが再描画され、Capture は true に設定されます。

WM_PAINT

WM_Paint_Handler - 適切な状態でボタンが再描画されます。

このようなマネージ コールバック メソッドでは、DrawButton メソッドを使用して、適切な状態のボタンを描画します。このメソッドには、この例で使用するウィンドウ、または Graphics オブジェクトのどちらかでボタンを描画するオーバーロードが 2 つあります。どちらのオーバーロードも、ボタンが押されたときに true のブール値を使用します。

GradientFilledButton クラスでは、GradientFill クラスを使用し、ネイティブ コードに対するプラットフォーム呼び出しを実行して、塗りつぶしを実行します。GradientFill クラスには、開始色と終了色を設定するプロパティと、塗りつぶし方向 (左から右または上から下) を指定するプロパティがあります。

参照

処理手順

方法 : Windows プロシージャをフックするためのクラスを使用する

方法 : プラットフォーム呼び出しにヘルパー クラスを使用する

方法 : ネイティブのコールバックを使用して TreeView をサブクラス化する

方法 : グラデーションの塗りつぶしを表示する

概念

.NET Compact Framework に関する「方法」トピック

その他の技術情報

.NET Compact Framework の相互運用性