C++ デスクトップ (Win32) アプリでの WinRT XAML ホスティング API の使用

Windows 10 バージョン 1903 以降、UWP 以外のデスクトップ アプリ (C++ デスクトップ (Win32)、WPF、Windows フォーム アプリを含む) では、"WinRT XAML ホスティング API" を使用して、ウィンドウ ハンドル (HWND) に関連付けられた任意の UI 要素の WinRT XAML コントロールをホストできます。 この API により、UWP 以外のデスクトップ アプリで、WinRT XAML コントロール経由でのみ使用できる最新の Windows 10 UI 機能を使用できるようにすることができます。 たとえば、UWP 以外のデスクトップ アプリはこの API を使用して、Fluent Design System を使用し、Windows Ink をサポートする WinRT XAML コントロールをホストできます。

WinRT XAML ホスティング API により、開発者が UWP 以外のデスクトップ アプリに Fluent UI を導入できるように用意されているより幅広い一連のコントロールのための基礎が提供されます。 この機能は XAML Islands と呼ばれます。 この機能の概要については、「デスクトップ アプリで WinRT XAML コントロールをホストする (XAML Islands)」を参照してください。

注意

XAML Islands に関するフィードバックがある場合は、Microsoft.Toolkit.Win32 リポジトリに新しい問題を作成し、そこにコメントを残してください。

WinRT XAML ホスティング API はデスクトップ アプリのための適切な選択肢ですか?

WinRT XAML ホスティング API により、デスクトップ アプリで WinRT XAML コントロールをホストするための低レベルのインフラストラクチャが提供されます。 デスクトップ アプリの種類によっては、別のより便利な API を使用してこの目標を達成するオプションがある場合があります。

  • C++ デスクトップ アプリがあり、そのアプリで WinRT XAML コントロールをホストしたい場合は、WinRT XAML ホスティング API を使用する必要があります。 これらのアプリの種類には別の手段がありません。

  • WPF および Windows フォーム アプリの場合は、直接 WinRT XAML ホスティング API を使用するのではなく、Windows Community Toolkit の XAML Island .NET コントロールを使用することを強くお勧めします。 これらのコントロールでは、内部的に WinRT XAML ホスティング API が使用され、キーボード ナビゲーションやレイアウト変更などの、直接 WinRT XAML ホスティング API を使用している場合には自分で処理する必要があるすべての動作が実装されています。

WinRT XAML ホスティング API は C++ デスクトップ アプリでのみ使用することが推奨されるため、この記事では主に、C++ デスクトップ アプリのための指示と例について説明します。 ただし、必要に応じて WPF および Windows フォーム アプリでも WinRT XAML ホスティング API を使用できます。 この記事では、Windows Community Toolkit の WPF と Windows フォーム用のホスト コントロールの関連するソース コードを示しているため、これらのコントロールで WinRT XAML ホスティング API がどのように使用されているかを確認できます。

XAML ホスティング API の使用方法を確認する

C++ デスクトップ アプリで XAML ホスティング API を使用するためのコード例の詳細な手順に従うには、次の記事を参照してください。

サンプル

コードで WinRT XAML ホスティング API を使用する方法は、アプリの種類やアプリの設計などの要因によって異なります。 完全なアプリのコンテキストでこの API を使用する方法を示すために、この記事では次のサンプルのコードを参照します。

C++ デスクトップ (Win32)

次のサンプルは、C++ デスクトップ アプリで WinRT XAML ホスティング API を使用する方法を示しています。

  • 単純な XAML Island のサンプル。 このサンプルは、パッケージ化されていない C++ デスクトップ アプリ (つまり、MSIX パッケージに組み込まれていないアプリ) での WinRT XAML コントロールのホスティングの基本的な実装を示しています。

  • カスタム コントロール サンプルを含む XAML Island。 このサンプルは、パッケージ化された C++ デスクトップ アプリでのカスタム WinRT XAML コントロールのホスティングのほか、キーボード入力やフォーカス ナビゲーションなどのその他の動作の処理の完全な実装を示しています。

WPF と Windows フォーム

Windows Community Toolkit の WindowsXamlHost コントロールは、WPF および Windows フォーム アプリで WinRT XAML ホスティング API を使用するためのリファレンス サンプルとして機能します。 このソース コードは、次の場所で入手できます。

注意

WPF および Windows フォーム アプリで直接 WinRT XAML ホスティング API を使用するのではなく、Windows Community Toolkit の XAML Island .NET コントロールを使用することを強くお勧めします。 この記事の WPF と Windows フォームのサンプル リンクは、説明のためにのみ示されています。

API のアーキテクチャ

WinRT XAML ホスティング API には、次の主な Windows ランタイム型と COM インターフェイスが含まれています。

型またはインターフェイス 説明
WindowsXamlManager このクラスは、UWP XAML フレームワークを表します。 このクラスは、デスクトップ アプリの現在のスレッドで UWP XAML フレームワークを初期化する 1 つの静的 InitializeForCurrentThread メソッドを提供します。
DesktopWindowXamlSource このクラスは、デスクトップ アプリでホストしている UWP XAML コンテンツのインスタンスを表します。 このクラスの最も重要なメンバーは、Content プロパティです。 このプロパティを、ホストする Windows.UI.Xaml.UIElement に割り当てます。 このクラスにはまた、XAML Islands との間でキーボード フォーカス ナビゲーションをルーティングするための他のメンバーも含まれています。
IDesktopWindowXamlSourceNative この COM インターフェイスは、アプリ内の XAML Island を親 UI 要素にアタッチするために使用する AttachToWindow メソッドを提供します。 すべての DesktopWindowXamlSource オブジェクトがこのインターフェイスを実装しています。
IDesktopWindowXamlSourceNative2 この COM インターフェイスは、UWP XAML フレームワークが特定の Windows メッセージを適切に処理できるようにする PreTranslateMessage メソッドを提供します。 すべての DesktopWindowXamlSource オブジェクトがこのインターフェイスを実装しています。

次の図は、デスクトップ アプリでホストされている XAML Island 内のオブジェクトの階層を示しています。

  • 基本レベルには、XAML Island をホストするアプリ内の UI 要素があります。 この UI 要素にはウィンドウ ハンドル (HWND) が必要です。 XAML Island をホストできる UI 要素の例には、C++ デスクトップ アプリのウィンドウ、WPF アプリの System.Windows.Interop.HwndHost、Windows フォーム アプリの System.Windows.Forms.Control などがあります。

  • 次のレベルには、DesktopWindowXamlSource オブジェクトがあります。 このオブジェクトは、XAML Island をホストするためのインフラストラクチャを提供します。 このオブジェクトを作成し、それを親 UI 要素にアタッチするのはユーザー コードの役割です。

  • DesktopWindowXamlSource を作成すると、このオブジェクトにより、WinRT XAML コントロールをホストするためのネイティブ子ウィンドウが自動的に作成されます。 このネイティブ子ウィンドウはユーザー コードからほとんど抽象化されていますが、必要に応じてそのハンドル (HWND) にアクセスできます。

  • 最後に、最上位レベルには、デスクトップ アプリでホストする WinRT XAML コントロールがあります。 これは、Windows SDK によって提供される WinRT XAML コントロールやカスタム ユーザー コントロールなど、Windows.UI.Xaml.UIElement から派生する任意の UWP オブジェクトになります。

DesktopWindowXamlSource のアーキテクチャ

注意

デスクトップ アプリで XAML Islands をホストすると、XAML コンテンツの複数のツリーを同じスレッド上で同時に実行できます。 XAML Island で XAML コンテンツのツリーのルート要素にアクセスし、それがホストされているコンテキストに関する関連情報を取得するには、XamlRoot クラスを使用します。 CoreWindowApplicationViewWindow の各 API では、XAML Islands に関する正しい情報が提供されません。 詳細については、こちらのセクションをご覧ください。

ベスト プラクティス

WinRT XAML ホスティング API を使用する場合は、WinRT XAML コントロールをホストする各スレッドで、次のベスト プラクティスに従ってください。

  • スレッドに専用の WindowsXamlManager を作成します。
  • ホストする各 WinRT XAML コントロールに、DesktopWindowXamlSource を作成します。
  • 不要になったら、各 DesktopWindowXamlSource は破棄します。
  • スレッドを終了する前に、そのスレッド専用の WindowsXamlManager を破棄します。 この WindowsXamlManager の破棄は非同期であり、スレッドを終了する前に Windows メッセージのキューをドレインする必要があることにご注意ください。 これを行う方法の例については、XAML アイランドのサンプルに関するページを参照してください。
  • 特定のスレッドで WindowsXamlManager を破棄した後に、同じスレッドで新しい WindowsXamlManager を作成することはサポートされておらず、予期しない動作が発生します。

トラブルシューティング

UWP アプリで WinRT XAML ホスティング API を使用した場合のエラー

問題 解決方法
アプリで次のメッセージの COMException を受信します。"DesktopWindowXamlSource をアクティブ化できません。 この型は UWP アプリでは使用できません。" または "WindowsXamlManager をアクティブ化できません。 この型は UWP アプリでは使用できません。" このエラーは、UWP アプリで WinRT XAML ホスティング API を使用しようとしている (具体的には、DesktopWindowXamlSource または WindowsXamlManager 型をインスタンス化しようとしている) ことを示しています。 WinRT XAML ホスティング API は、UWP 以外のデスクトップ アプリ (WPF、Windows フォーム、C++ デスクトップ アプリケーションなど) で使用されることのみを目的にしています。

WindowsXamlManager または DesktopWindowXamlSource 型を使用しようとしているときのエラー

問題 解決方法
アプリで次のメッセージの例外を受信します。"WindowsXamlManager および DesktopWindowXamlSource は、Windows バージョン 10.0.18226.0 以降を対象とするアプリでサポートされています。 アプリケーション マニフェストまたはパッケージ マニフェストを確認し、MaxTestedVersion プロパティが更新されていることを確認してください。" このエラーは、アプリケーションが WinRT XAML ホスティング API で WindowsXamlManager または DesktopWindowXamlSource 型を使用しようとしたが、そのアプリが Windows 10 バージョン 1903 以降を対象としてビルドされたかどうかを OS が判断できないことを示しています。 WinRT XAML ホスティング API は最初、以前のバージョンの Windows 10 でプレビューとして導入されましたが、Windows 10 バージョン 1903 以降でのみサポートされています。

この問題を解決するには、そのアプリの MSIX パッケージを作成し、それをパッケージから実行するか、または Microsoft.Toolkit.Win32.UI.SDK NuGet パッケージをプロジェクトにインストールします。

別のスレッドのウィンドウにアタッチしているときのエラー

問題 解決方法
アプリで次のメッセージの COMException を受信します。"指定された HWND が別のスレッドで作成されたため、AttachToWindow メソッドが失敗しました。" このエラーは、アプリケーションが IDesktopWindowXamlSourceNative::AttachToWindow メソッドを呼び出し、それに別のスレッドで作成されたウィンドウの HWND を渡したことを示しています。 このメソッドには、そのメソッドを呼び出しているコードと同じスレッドで作成されたウィンドウの HWND を渡す必要があります。

別のトップレベル ウィンドウのウィンドウにアタッチしているときのエラー

問題 解決方法
アプリで次のメッセージの COMException を受信します。"指定された HWND が、以前に同じスレッドで AttachToWindow に渡された HWND とは別のトップレベル ウィンドウから派生しているため、AttachToWindow メソッドが失敗しました。" このエラーは、アプリケーションが IDesktopWindowXamlSourceNative::AttachToWindow メソッドを呼び出し、それに同じスレッドでのこのメソッドへの以前の呼び出しで指定されたウィンドウとは別のトップレベル ウィンドウから派生しているウィンドウの HWND を渡したことを示しています。

アプリケーションが特定のスレッドで AttachToWindow を呼び出した後、同じスレッドのその他のすべての DesktopWindowXamlSource オブジェクトは、AttachToWindow への最初の呼び出しで渡されたのと同じトップレベル ウィンドウの子孫であるウィンドウにのみアタッチできます。 特定のスレッドのすべての DesktopWindowXamlSource オブジェクトが閉じられた後は、次の DesktopWindowXamlSource を再び任意のウィンドウに自由にアタッチできます。

この問題を解決するには、このスレッドの他のトップレベル ウィンドウにバインドされているすべての DesktopWindowXamlSource オブジェクトを閉じるか、またはこの DesktopWindowXamlSource のための新しいスレッドを作成します。