Important
このトピックでは、CommunityToolkit/Microsoft.Toolkit.Win32 GitHub リポジトリの型を使用またはメンションします。 UWP XAML Islands のサポートに関する重要な情報については、そのリポジトリの XAML Islands Notice を参照してください。
この記事では、UWP XAML ホスティング API を使用して、新しい C++ デスクトップ アプリで標準の UWP XAML コントロール (つまり、Windows SDK によって提供されるコントロール) をホストする方法について説明します。 このコードは、simple XAML Island サンプルに基づいており、このセクションでは、コードの最も重要な部分について説明します。 既存の C++ デスクトップ アプリprojectがある場合は、これらの手順とコード例をprojectに合わせて調整できます。
注
この記事で示すシナリオでは、アプリでホストされている UWP XAML コントロールの XAML マークアップを直接編集することはできません。 このシナリオは、ホストされているコントロールの外観と動作をコードで変更することに限定されています。 UWP XAML コントロールをホストするときに XAML マークアップを直接編集できるようにする手順については、「 C++ デスクトップ アプリでカスタム UWP XAML コントロールをホストする」を参照してください。
デスクトップ アプリケーション プロジェクトを作成する
Windows 10バージョン 1903 SDK (ビルド 10.0.18362) 以降のリリースがインストールされた Visual Studio 2019 では、新しい Windows デスクトップ アプリケーション projectを作成し、MyDesktopWin32App という名前を付けます。 このprojectの種類は、C++、Windows、および Desktop project フィルターで使用できます。
ソリューション エクスプローラーでは、 ソリューション ノードを右クリックし、Retarget solution をクリックし、10.0.18362.0 以降の SDK リリースを選択して、OK をクリックします。
Microsoft.Windows.CppWinRT NuGet パッケージをインストールして、projectで C++/WinRT のサポートを有効にします。
ソリューション エクスプローラー Manage NuGet パッケージ を選択します。- [参照] タブを選択し、Microsoft.Windows.CppWinRT パッケージを探して、このパッケージの最新バージョンをインストールします。
注
新しいプロジェクトの場合は、C++/WinRT Visual Studio Extension (VSIX)をインストールし>その拡張機能に含まれている C++/WinRT project テンプレートのいずれかを使用できます。 詳細については、Visual Studio C++/WinRT のサポートと VSIX に関するページを参照してください。
NuGet パッケージ マネージャー ウィンドウの タブで、Browse Microsoft.Toolkit.Win32.UI.SDK NuGet パッケージを検索し、このパッケージの最新の安定バージョンをインストールします。 このパッケージには、UWP XAML Islands をアプリで動作させるビルドと実行時のアセットがいくつか用意されています。アプリケーションが Windows 10 バージョン 1903 と互換性があることを指定するには、
maxversiontestedで 値を設定します。projectにアプリケーション マニフェストがまだない場合は、projectに新しい XML ファイルを追加し、app.manifest という名前を付けます。
次の例に示すように、compatibility 要素と子要素をアプリケーション マニフェストに含めます。 maxversiontested 要素の Id 属性を、ターゲットとしている Windows のバージョン番号に置き換えます (これは 10.0.18362.0 以降のリリースである必要があります)。 値を大きい値に設定すると、Windows の古いバージョンでアプリが正しく実行されなくなることに注意してください。これは、どの Windows リリースにおいても、それより前のバージョンしか把握されないためです。 Windows 10バージョン 1903 (ビルド 10.0.18362) でアプリを実行する場合は、10.0.18362.0 の値をそのままにするか、アプリでサポートされるさまざまな値に対して複数の maxversiontested 要素を追加する必要があります。
<?xml version="1.0" encoding="UTF-8"?> <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> <application> <!-- Windows 10 --> <maxversiontested Id="10.0.18362.0"/> <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" /> </application> </compatibility> </assembly>
Windows ランタイム メタデータへの参照を追加します。
- ソリューション エクスプローラーで、project References ノードを右クリックし、参照の追加を選択します。
- ページの下部にある [参照] ボタンをクリックし、SDK インストール パスの UnionMetadata フォルダーに移動します。 既定では、SDK は
C:\Program Files (x86)\Windows Kits\10\UnionMetadataにインストールされます。 - 次に、対象とする Windows バージョン (例: 10.0.18362.0) の名前が付いたフォルダーを選択し、そのフォルダー内で
Windows.winmdファイルを選択します。 - [OK] をクリックし、 [参照の追加] ダイアログを閉じます。
XAML ホスティング API を使用して UWP XAML コントロールをホストする
XAML ホスティング API を使用して UWP XAML コントロールをホストする基本的なプロセスは、次の一般的な手順に従います。
アプリがホストするWindows.UI.Xaml.UIElementオブジェクトを作成する前に、現在のスレッドのUWP XAMLフレームワークを初期化します。 コントロールをホストするDesktopWindowXamlSourceオブジェクトを作成するタイミングによって、これを行う方法はいくつかあります。
アプリケーションで DesktopWindowXamlSource オブジェクトを作成し、その後に、そのオブジェクトによってホストされるいずれかの Windows.UI.Xaml.UIElement オブジェクトを作成する場合は、DesktopWindowXamlSource オブジェクトをインスタンス化するときに、このフレームワークが初期化されます。 このシナリオでは、フレームワークを初期化するためにご自身のコードを追加する必要はありません。
ただし、アプリケーションが Windows.UI.Xaml.UIElement オブジェクトを、これをホストすることになる DesktopWindowXamlSource オブジェクトよりも先に作成する場合、WindowsXamlManager.InitializeForCurrentThread メソッドを呼び出して、Windows.UI.Xaml.UIElement オブジェクトがインスタンス化される前に UWP XAML フレームワークを明示的に初期化する必要があります。 アプリケーションでは一般的に、DesktopWindowXamlSource をホストする親 UI 要素がインスタンス化されるときに、このメソッドを呼び出す必要があります。
注
このメソッドは、UWP XAML フレームワークへの参照を含む WindowsXamlManager オブジェクトを返します。 任意の 1 つのスレッドで WindowsXamlManager オブジェクトを必要な数だけ作成できます。 ただし、各オブジェクトは UWP XAML フレームワークへの参照を保持するため、XAML リソースが最終的に解放されるようにオブジェクトを破棄する必要があります。
DesktopWindowXamlSource オブジェクトを作成し、アプリケーション内のウィンドウ ハンドルに関連付けられている親 UI 要素にアタッチします。
これを行うには、次の手順に従う必要があります。
DesktopWindowXamlSource オブジェクトを作成し、それを IDesktopWindowXamlSourceNative または IDesktopWindowXamlSourceNative2 COM インターフェイスにキャストします。
IDesktopWindowXamlSourceNative または IDesktopWindowXamlSourceNative2 インターフェイスの AttachToWindow メソッドを呼び出し、アプリケーション内の親 UI 要素のウィンドウ ハンドルを渡します。
Important
AttachToWindow メソッドが、DesktopWindowXamlSource オブジェクトごとに 1 回のみ呼び出されるようにしてください。 1 つの DesktopWindowXamlSource オブジェクトに対してこのメソッドを複数回呼び出すと、メモリ リークが発生する可能性があります。
DesktopWindowXamlSource に含まれる内部の子ウィンドウの初期サイズを設定します。 既定では、この内部の子ウィンドウは、幅と高さが 0 に設定されています。 ウィンドウのサイズを設定しない場合、 DesktopWindowXamlSource に追加した UWP XAML コントロールは表示されません。
DesktopWindowXamlSource で内部子ウィンドウをaccessするには、IDesktopWindowXamlSourceNative > またはIDesktopWindowXamlSourceNative2 インターフェイスの プロパティを使用します。WindowHandle
最後に、ホストする対象の Windows.UI.Xaml.UIElement を DesktopWindowXamlSource オブジェクトの Content プロパティに割り当てます。
次の手順とコード例では、前述のプロセスを実装する方法を示します。
projectの Source Files フォルダーで、既定の MyDesktopWin32App.cpp ファイルを開きます。 ファイルの内容全体を削除し、次の
includeおよびusingステートメントを追加します。 これらのステートメントには、標準の C++ および UWP ヘッダーと名前空間に加えて、UWP XAML Islands に固有のいくつかの項目が含まれています。#include <windows.h> #include <stdlib.h> #include <string.h> #include <winrt/Windows.Foundation.Collections.h> #include <winrt/Windows.system.h> #include <winrt/windows.ui.xaml.hosting.h> #include <windows.ui.xaml.hosting.desktopwindowxamlsource.h> #include <winrt/windows.ui.xaml.controls.h> #include <winrt/Windows.ui.xaml.media.h> using namespace winrt; using namespace Windows::UI; using namespace Windows::UI::Composition; using namespace Windows::UI::Xaml::Hosting; using namespace Windows::Foundation::Numerics;前のセクションの後に次のコードをコピーします。 このコードでは、アプリの WinMain 関数を定義します。 この関数によって基本的なウィンドウを初期化し、XAML ホスティング API を使用して、ウィンドウ内の単純な UWP TextBlock コントロールをホストします。
LRESULT CALLBACK WindowProc(HWND, UINT, WPARAM, LPARAM); HWND _hWnd; HWND _childhWnd; HINSTANCE _hInstance; int CALLBACK WinMain(_In_ HINSTANCE hInstance, _In_opt_ HINSTANCE hPrevInstance, _In_ LPSTR lpCmdLine, _In_ int nCmdShow) { _hInstance = hInstance; // The main window class name. const wchar_t szWindowClass[] = L"Win32DesktopApp"; WNDCLASSEX windowClass = { }; windowClass.cbSize = sizeof(WNDCLASSEX); windowClass.lpfnWndProc = WindowProc; windowClass.hInstance = hInstance; windowClass.lpszClassName = szWindowClass; windowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); windowClass.hIconSm = LoadIcon(windowClass.hInstance, IDI_APPLICATION); if (RegisterClassEx(&windowClass) == NULL) { MessageBox(NULL, L"Windows registration failed!", L"Error", NULL); return 0; } _hWnd = CreateWindow( szWindowClass, L"Windows c++ Win32 Desktop App", WS_OVERLAPPEDWINDOW | WS_VISIBLE, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL ); if (_hWnd == NULL) { MessageBox(NULL, L"Call to CreateWindow failed!", L"Error", NULL); return 0; } // Begin XAML Island section. // The call to winrt::init_apartment initializes COM; by default, in a multithreaded apartment. winrt::init_apartment(apartment_type::single_threaded); // Initialize the XAML framework's core window for the current thread. WindowsXamlManager winxamlmanager = WindowsXamlManager::InitializeForCurrentThread(); // This DesktopWindowXamlSource is the object that enables a non-UWP desktop application // to host UWP XAML controls in any UI element that is associated with a window handle (HWND). DesktopWindowXamlSource desktopSource; // Get handle to the core window. auto interop = desktopSource.as<IDesktopWindowXamlSourceNative>(); // Parent the DesktopWindowXamlSource object to the current window. check_hresult(interop->AttachToWindow(_hWnd)); // This HWND will be the window handler for the XAML Island: A child window that contains XAML. HWND hWndXamlIsland = nullptr; // Get the new child window's HWND. interop->get_WindowHandle(&hWndXamlIsland); // Update the XAML Island window size because initially it is 0,0. SetWindowPos(hWndXamlIsland, 0, 200, 100, 800, 200, SWP_SHOWWINDOW); // Create the XAML content. Windows::UI::Xaml::Controls::StackPanel xamlContainer; xamlContainer.Background(Windows::UI::Xaml::Media::SolidColorBrush{ Windows::UI::Colors::LightGray() }); Windows::UI::Xaml::Controls::TextBlock tb; tb.Text(L"Hello World from Xaml Islands!"); tb.VerticalAlignment(Windows::UI::Xaml::VerticalAlignment::Center); tb.HorizontalAlignment(Windows::UI::Xaml::HorizontalAlignment::Center); tb.FontSize(48); xamlContainer.Children().Append(tb); xamlContainer.UpdateLayout(); desktopSource.Content(xamlContainer); // End XAML Island section. ShowWindow(_hWnd, nCmdShow); UpdateWindow(_hWnd); //Message loop: MSG msg = { }; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return 0; }前のセクションの後に次のコードをコピーします。 このコードでは、ウィンドウのウィンドウ プロシージャを定義します。
LRESULT CALLBACK WindowProc(HWND hWnd, UINT messageCode, WPARAM wParam, LPARAM lParam) { PAINTSTRUCT ps; HDC hdc; wchar_t greeting[] = L"Hello World in Win32!"; RECT rcClient; switch (messageCode) { case WM_PAINT: if (hWnd == _hWnd) { hdc = BeginPaint(hWnd, &ps); TextOut(hdc, 300, 5, greeting, wcslen(greeting)); EndPaint(hWnd, &ps); } break; case WM_DESTROY: PostQuitMessage(0); break; // Create main window case WM_CREATE: _childhWnd = CreateWindowEx(0, L"ChildWClass", NULL, WS_CHILD | WS_BORDER, 0, 0, 0, 0, hWnd, NULL, _hInstance, NULL); return 0; // Main window changed size case WM_SIZE: // Get the dimensions of the main window's client // area, and enumerate the child windows. Pass the // dimensions to the child windows during enumeration. GetClientRect(hWnd, &rcClient); MoveWindow(_childhWnd, 200, 200, 400, 500, TRUE); ShowWindow(_childhWnd, SW_SHOW); return 0; // Process other messages. default: return DefWindowProc(hWnd, messageCode, wParam, lParam); break; } return 0; }コード ファイルを保存し、アプリをビルドして実行します。 アプリ ウィンドウに UWP TextBlock コントロールが表示されることを確認します。
注
warning C4002: too many arguments for function-like macro invocation 'GetCurrentTime'やmanifest authoring warning 81010002: Unrecognized Element "maxversiontested" in namespace "urn:schemas-microsoft-com:compatibility.v1"など、いくつかのビルド警告が表示される場合があります。 これらの警告は、現在のツールと NuGet パッケージに関する既知の問題であり、無視してかまいません。
XAML ホスティング API を使用して UWP XAML コントロールをホストする完全な例については、次のコード ファイルを参照してください。
- C++ デスクトップ (Win32):UWP XAML Islands コード サンプル リポジトリのXamlBridge.cpp ファイルを参照してください。
- WPF: Windows Community Toolkit の WindowsXamlHostBase.cs ファイルと WindowsXamlHost.cs ファイルを参照してください。
- Windows フォーム: Windows Community Toolkit の WindowsXamlHostBase.cs および WindowsXamlHost.cs ファイルを参照してください。
アプリをパッケージ化する
必要に応じて、配置用にアプリを MSIX パッケージにパッケージ化することもできます。 MSIX は、Windows 向けの最新のアプリ パッケージ化テクノロジであり、MSI、.appx、App-V、ClickOnce インストールの各テクノロジの組み合わせが基になっています。
次の手順では、Visual Studio 2019 の Windows アプリケーション パッケージ Project を使用して、ソリューション内のすべてのコンポーネントを MSIX パッケージにパッケージ化する方法について説明します。 これらの手順は、アプリを MSIX パッケージにパッケージ化する場合にのみ必要です。
注
展開用にアプリケーションを MSIX パッケージにパッケージ化しない場合は、アプリを実行するコンピューターに Visual C++ ランタイムがインストールされている必要があります。
新しい Windows アプリケーション パッケージ Project をソリューションに追加します。 プロジェクトを作成するときに、Windows 10 バージョン 1903 (10.0;ビルド 18362) を Target version と Minimum version の両方に選択してください。
パッケージ化projectで、Applications ノードを右クリックし、参照の追加を選択します。 プロジェクトの一覧で、ソリューションで C++ デスクトップ アプリケーション projectを選択し、OK をクリックします。
パッケージングプロジェクトをビルドして実行します。 アプリが実行され、UWP XAML コントロールが期待どおりに表示されることを確認します。
パッケージの配布/展開の詳細については、「MSIX の展開を管理する」を参照してください。
次のステップ
この記事のコード例では、C++ デスクトップ アプリで標準の UWP XAML コントロールをホストする基本的なシナリオを開始します。 以下のセクションでは、アプリケーションでサポートすることが必要になる可能性のある追加のシナリオを紹介します。
カスタム UWP XAML コントロールをホストする
多くのシナリオでは、連携する複数の個々のコントロールを含むカスタム UWP XAML コントロールをホストすることが必要になる場合があります。 C++ デスクトップ アプリでカスタム コントロール (自身で定義したコントロール、またはサードパーティによって提供されるコントロール) をホストするプロセスは、標準コントロールをホストするよりも複雑であり、追加のコードが必要です。
完全なチュートリアルについては、「 XAML ホスティング API を使用して C++ デスクトップ アプリでカスタム UWP XAML コントロールをホストする」を参照してください。
高度なシナリオ
UWP XAML Islands をホストする多くのデスクトップ アプリケーションでは、スムーズなユーザー エクスペリエンスを提供するために、追加のシナリオを処理する必要があります。 たとえば、デスクトップ アプリケーションでは、UWP XAML Islands でのキーボード入力、UWP XAML Islands とその他の UI 要素間のフォーカス ナビゲーション、レイアウトの変更を処理する必要がある場合があります。
これらのシナリオと関連するコード サンプルへのポインターの処理の詳細については、「 UWP XAML Islands の高度なシナリオ」を参照してください。