Windows フォームと WPF の相互運用性入力アーキテクチャ

WPF と Windows フォーム間の相互運用には、両方のテクノロジに適切なキーボード入力処理が必要です。 このトピックでは、これらのテクノロジでどのようにキーボードおよびメッセージ処理を実装し、ハイブリッド アプリケーションでスムーズな相互運用を可能にする方法について説明します。

このトピックは、次の内容で構成されています。

  • モードレス フォームとダイアログ ボックス

  • WindowsFormsHost のキーボードとメッセージの処理

  • ElementHost のキーボードとメッセージの処理

モードレス フォームとダイアログ ボックス

WindowsFormsHost 要素に対して EnableWindowsFormsInterop メソッドを呼び出して、WPF ベースのアプリケーションからモードレス フォームまたはダイアログ ボックスを開きます。

ElementHost コントロールに対して EnableModelessKeyboardInterop メソッドを呼び出して、Windows フォーム ベースのアプリケーションでモードレス WPF ページを開きます。

WindowsFormsHost のキーボードとメッセージの処理

WPF ベースのアプリケーションでホストされている場合、Windows フォームのキーボードとメッセージの処理は以下で構成されます。

  • WindowsFormsHost クラスでは、ComponentDispatcher クラスによって実装されている WPF メッセージ ループからメッセージを取得します。

  • WindowsFormsHost クラスでは、サロゲート Windows フォーム メッセージ ループを作成し、通常の Windows フォーム キーボード処理が確実に行われるようにします。

  • WindowsFormsHost クラスでは、IKeyboardInputSink インターフェイスを実装し、WPF を使用してフォーカス管理を調整します。

  • WindowsFormsHost コントロールでは、自身を登録し、メッセージ ループを開始します。

以下のセクションでは、プロセスのこれらの部分について詳しく説明します。

WPF メッセージ ループからのメッセージの取得

ComponentDispatcher クラスでは、WPF のメッセージ ループ マネージャーを実装します。 ComponentDispatcher クラスには、WPF による処理前に外部クライアントでメッセージをフィルター処理できるようになるフックが用意されています。

相互運用の実装では ComponentDispatcher.ThreadFilterMessage イベントを処理します。これにより、Windows フォーム コントロールで WPF コントロールの前にメッセージを処理できるようになります。

サロゲート Windows フォーム メッセージ ループ

既定で、System.Windows.Forms.Application クラスには Windows フォーム アプリケーションのプライマリ メッセージ ループが含まれています。 相互運用中、Windows フォーム メッセージ ループによってメッセージは処理されません。 そのため、このロジックを再現する必要があります。 ComponentDispatcher.ThreadFilterMessage イベントのハンドラーでは、以下の手順が実行されます。

  1. IMessageFilter インターフェイスを使用してメッセージをフィルター処理します。

  2. Control.PreProcessMessage メソッドを呼び出します。

  3. 必要に応じて、メッセージを変換してディスパッチします。

  4. 他のコントロールでメッセージが処理されない場合、メッセージをホスティング コントロールに渡します。

IKeyboardInputSink の実装

サロゲート メッセージ ループでは、キーボード管理を処理します。 そのため、IKeyboardInputSink.TabInto メソッドは、WindowsFormsHost クラスでの実装を必要とする唯一の IKeyboardInputSink メンバーです。

既定では、HwndHost クラスから、その IKeyboardInputSink.TabInto 実装に対して false が返されます。 これにより、WPF コントロールから Windows フォーム コントロールにタブ移動できなくなります。

IKeyboardInputSink.TabInto メソッドの WindowsFormsHost の実装では、次の手順を実行します。

  1. WindowsFormsHost コントロールに含まれ、フォーカスを受け取ることができる最初または最後の Windows フォーム コントロールを見つけます。 コントロールの選択は、走査情報によって変わります。

  2. コントロールにフォーカスを設定し、true を返します。

  3. コントロールがフォーカスを受け取ることができない場合は、false を返します。

WindowsFormsHost の登録

WindowsFormsHost コントロールへのウィンドウ ハンドルが作成されると、WindowsFormsHost コントロールから、メッセージ ループの存在を登録する内部静的メソッドが呼び出されます。

登録時に、WindowsFormsHost コントロールによってメッセージ ループが調べられます。 メッセージ ループが開始されていない場合は、ComponentDispatcher.ThreadFilterMessage イベント ハンドラーが作成されます。 メッセージ ループは、ComponentDispatcher.ThreadFilterMessage イベント ハンドラーがアタッチされているときに実行されていると見なされます。

ウィンドウ ハンドルが破棄されると、WindowsFormsHost コントロールが登録から削除されます。

ElementHost のキーボードとメッセージの処理

Windows フォーム アプリケーションでホストされている場合、WPF キーボードとメッセージの処理は以下で構成されます。

  • HwndSourceIKeyboardInputSink、および IKeyboardInputSite インターフェイスの実装。

  • タブ移動と方向キー

  • コマンド キーとダイアログ ボックスのキー。

  • Windows フォーム アクセラレータ処理。

以下のセクションでは、これらの部分について詳しく説明します。

インターフェイスの実装

Windows フォームでは、キーボード メッセージはフォーカスのあるコントロールのウィンドウ ハンドルにルーティングされます。 ElementHost コントロールでは、これらのメッセージはホストされている要素にルーティングされます。 これを実現するために、ElementHost コントロールには HwndSource インスタンスが用意されています。 ElementHost コントロールにフォーカスがある場合、HwndSource インスタンスによってほとんどのキーボード入力がルーティングされ、WPF InputManager クラスで処理できるようになります。

HwndSource クラスには、IKeyboardInputSink および IKeyboardInputSite インターフェイスが実装されています。

キーボードの相互運用は、ホストされている要素からフォーカスを移動する Tab キーと方向キーの入力を処理する OnNoMoreTabStops メソッドの実装に依存しています。

タブ移動と方向キー

Windows フォーム選択ロジックは、Tab キーと方向キーのナビゲーションを実装するために、IKeyboardInputSink.TabInto および OnNoMoreTabStops メソッドにマップされます。 Select メソッドをオーバーライドすると、このマッピングが行われます。

コマンド キーとダイアログ ボックス キー。

コマンド キーとダイアログ キーを処理する最初の機会を WPF に与えるために、Windows フォーム コマンドの前処理は TranslateAccelerator メソッドに接続されています。 Control.ProcessCmdKey メソッドをオーバーライドすると、2 つのテクノロジが接続されます。

TranslateAccelerator メソッドを使用すると、ホストされている要素では、WM_KEYDOWN、WM_KEYUP、WM_SYSKEYDOWN、WM_SYSKEYUP などの任意のキー メッセージ (Tab キー、Enter キー、Esc キー、方向キーなどのコマンド キーを含む) を処理できます。 キー メッセージが処理されない場合は、処理のために Windows フォームの先祖階層が送信されます。

アクセラレータの処理

アクセラレータを正しく処理するには、Windows フォーム アクセラレータ処理を WPF AccessKeyManager クラスに接続する必要があります。 さらに、すべての WM_CHAR メッセージは、ホストされている要素に正しくルーティングされる必要があります。

TranslateChar メソッドの既定の HwndSource 実装からは false が返されるため、WM_CHAR メッセージは次のロジックを使用して処理されます。

  • すべての WM_CHAR メッセージがホストされる要素に確実に転送されるように、Control.IsInputChar メソッドはオーバーライドされます。

  • Alt キーが押された場合、メッセージは WM_SYSCHAR です。 Windows フォームでは、IsInputChar メソッドを通じてこのメッセージが前処理されません。 そのため、ProcessMnemonic メソッドは、登録されたアクセラレータの WPF AccessKeyManager のクエリを実行するためにオーバーライドされます。 登録されているアクセラレータが見つかった場合、AccessKeyManager によってそれが処理されます。

  • Alt キーが押されていない場合、WPF InputManager クラスによって未処理の入力が処理されます。 入力がアクセラレータである場合、AccessKeyManager によって処理されます。 PostProcessInput イベントは、処理されなかった WM_CHAR メッセージに対して処理されます。

ユーザーが Alt キーを押すと、フォーム全体にアクセラレータの視覚的な合図が表示されます。 この動作をサポートするために、アクティブなフォームのすべての ElementHost コントロールは、フォーカスのあるコントロールに関係なく、WM_SYSKEYDOWN メッセージを受け取ります。

メッセージはアクティブ フォームの ElementHost コントロールにのみ送信されます。

関連項目