這很重要
本主題使用或提及 CommunityToolkit/Microsoft.Toolkit.Win32 GitHub 倉庫中的類型。 有關 XAML 島嶼支援的重要資訊,請參閱該倉庫中的 XAML 島嶼通知。
此文章示範如何使用 WinRT XAML 裝載 API,在新的 C++ 傳統型應用程式中裝載標準 WinRT XAML 控制項 (也就是 Windows SDK 所提供的控制項)。 程式碼基於 simple XAML Island 範例,本節討論程式碼中一些最重要的部分。 如果你已有 C++ 桌面應用程式 project,可以將這些步驟和程式碼範例應用到你的 project。
備註
此文章示範的案例不支援直接編輯應用程式中裝載之 WinRT XAML 控制項的 XAML 標記。 此案例限制您只能透過程式碼,修改裝載控制項的外觀和行為。 如需可讓您在裝載 WinRT XAML 控制項時直接編輯 XAML 標記的指示,請參閱在 C++ 傳統型應用程式中裝載自訂 WinRT XAML 控制項。
建立桌面應用程式專案
在安裝 Windows 10 版本 1903 SDK(版本 10.0.18362)或更新版本的 Visual Studio 2019 中,建立一個新的 Windows 桌面應用程式 專案,並命名為 MyDesktopWin32App。 此 project 類型可在 C++、Windows 以及 Desktop project 篩選器中取得。
在 Solution Explorer,右鍵點選解決方案節點,點選 Retarget 解決方案,選擇 10.0.18362.0或更新版本的 SDK 版本,然後點選 OK。
安裝 Microsoft.Windows.CppWinRT NuGet 套件,以在您的專案中啟用對 C++/WinRT 的支援:
- 在Solution Explorer中對你的專案點擊右鍵,選擇管理 NuGet 套件。
- 選取 [瀏覽] 索引標籤、搜尋 Microsoft.Windows.CppWinRT \(英文\) 套件,然後安裝此套件的最新版本。
備註
對於新專案,你也可以安裝 C++/WinRT Visual Studio擴充套件(VSIX),並使用該擴充套件中包含的 C++/WinRT project範本。 更多細節請參見 Visual Studio 對 C++/WinRT 的支援,以及 VSIX。
在 NuGet Package Manager 視窗的 Browse 分頁中,搜尋 Microsoft.Toolkit.Win32.UI.SDK NuGet 套件,並安裝此套件的最新穩定版本。 此套件提供數個組建和執行階段資產,讓 XAML Islands 可在您的應用程式中運作。
在你的
maxversiontested中設定 值,指定你的應用程式與 Windows 10,版本 1903 相容。如果你的project還沒有應用程式清單,請在project中新增一個 XML 檔案,命名為 app.manifest。
在您的應用程式資訊清單中,包含下列範例所示的 compatibility 元素及子元素。 以您要設為目標的 Windows 版本號碼 (必須是 10.0.18362.0 或更新版本) 來取代 maxversiontested 元素的 Id 屬性。 請注意,設定較高的值意味著舊版 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 Runtime 元資料的參考:
- 在 Solution Explorer,右鍵點擊你的 project References節點,選擇 Add Reference。
- 按一下頁面底部的 [瀏覽] 按鈕,然後瀏覽至 SDK 安裝路徑中的 UnionMetadata 資料夾。 SDK 預設會安裝到
C:\Program Files (x86)\Windows Kits\10\UnionMetadata。 - 然後,選取以您要瞄準的 Windows 版本 (例如 10.0.18362.0) 所命名的資料夾,然後在該資料夾內挑選
Windows.winmd檔案。 - 按一下 [確定] 以關閉 [新增參考] 對話方塊。
使用 XAML 裝載 API 來裝載 WinRT XAML 控制項
使用 XAML 裝載 API 來裝載 WinRT XAML 控制項的基本程序會遵循下列一般步驟:
在您的應用程式建立其將裝載的任何 Windows.UI.Xaml.UIElement 物件之前,先為目前執行緒初始化 WinRT XAML 架構。 有幾種方法可以達成此目的,取決於您計劃何時建立DesktopWindowXamlSource物件作為控制項的容器。
如果您的應用程式在建立任何其將裝載的 Windows.UI.Xaml.UIElement 物件之前先建立 DesktopWindowXamlSource 物件,則會在您將 DesktopWindowXamlSource 物件具現化時,將此架構初始化。 在此案例中,您不需要新增自己的任何程式碼來將架構初始化。
不過,如果您的應用程式在建立將裝載 Windows.UI.Xaml.UIElement 物件的 DesktopWindowXamlSource 物件之前先建立了前者物件,則您的應用程式必須先呼叫靜態的 WindowsXamlManager.InitializeForCurrentThread 方法,明確地將 WinRT XAML 架構初始化,然後再將 Windows.UI.Xaml.UIElement 物件具現化。 將裝載 DesktopWindowXamlSource 的父 UI 元素具現化時,您的應用程式通常應該會呼叫此方法。
備註
此方法會傳回包含 WinRT XAML 架構參考的 WindowsXamlManager \(英文\) 物件。 您可以視需要在指定的執行緒上建立多個 WindowsXamlManager 物件。 不過,由於每個物件都持有 WinRT XAML 架構的參考,因此,您應該處置物件,以確保最終會釋放 XAML 資源。
建立 DesktopWindowXamlSource \(英文\) 物件,並將其附加至與視窗控制碼相關聯之應用程式中的父 UI 元素。
要做到這一點,你需要遵循以下步驟:
建立 DesktopWindowXamlSource 物件,並將其轉換成 IDesktopWindowXamlSourceNative 或 IDesktopWindowXamlSourceNative2 COM 介面。
呼叫 IDesktopWindowXamlSourceNative 或 IDesktopWindowXamlSourceNative2 介面的 AttachToWindow 方法,並傳入應用程式中父 UI 元素的視窗控制碼。
這很重要
請確定您的程式碼針對每個 DesktopWindowXamlSource 物件只會呼叫 AttachToWindow 方法一次。 針對 DesktopWindowXamlSource 物件多次呼叫此方法可能會導致記憶體流失。
設定 DesktopWindowXamlSource 中所含之內部子視窗的初始大小。 根據預設,這個內部子視窗的寬度和高度均設為 0。 如果您未設定視窗的大小,則您新增至 DesktopWindowXamlSource 的任何 WinRT XAML 控制項都不會顯示。 要access DesktopWindowXamlSource 的內部子視窗,請使用 IDesktopWindowXamlSourceNative 或 IDesktopWindowXamlSourceNative2 介面的 WindowHandle 屬性。
最後,將您想要裝載的 Windows.UI.Xaml.UIElement 指派給 DesktopWindowXamlSource 物件的 Content 屬性。
以下步驟與程式碼範例說明如何實作上述流程:
在project的 Source Files資料夾中,開啟預設的 MyDesktopWin32App.cpp 檔案。 刪除檔案的整個內容,並新增下列
include和using陳述式。 除了標準 C++ 和 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 WinRT 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 來裝載 WinRT XAML 控制項的完整範例,請參閱下列程式碼檔案:
- C++ 桌面(Win32):請參見 XAML Islands 程式碼範例 repo 中的 XamlBridge.cpp 檔案。
- WPF:請參閱 Windows 社群工具包中的 WindowsXamlHostBase.cs 和 WindowsXamlHost.cs 檔案。
- Windows Forms: 請參閱 Windows 社群工具包中的 WindowsXamlHostBase.cs 和 WindowsXamlHost.cs 檔案。
封裝應用程式
您可以選擇性地在 MSIX 套件中封裝應用程式以供部署。 MSIX 是 Windows 的新式應用程式封裝技術,以 MSI、.appx、App-V 和 ClickOnce 安裝技術的組合為基礎。
以下說明如何利用 Visual Studio 2019 中的 Windows 應用程式封裝專案,將解決方案中的所有元件封裝成 MSIX 套件。 只有當您想要在 MSIX 套件中封裝應用程式時,才需要執行這些步驟。
備註
如果您選擇不將應用程式封裝在 MSIX 封裝中進行部署,那麼執行您的應用程式的電腦必須安裝 Visual C++ Runtime。
在您的解決方案中新增一個新的 Windows 應用程式封裝專案。 建立專案時,選擇 Windows 10,版本 1903(10.0;Build 18362)作為目標版本和最低版本。
在打包 project 中,右鍵點擊 Applications 節點,選擇 Add reference。 在專案清單中,選擇您解決方案中的 C++ 桌面應用程式專案,然後點擊 OK。
建立並執行套件專案。 確認應用程式會如預期般執行並顯示 WinRT XAML 控制項。
如需散發/部署封裝的相關資訊,請參閱管理 MSIX 部署。
後續步驟
此文章中的程式碼範例可讓您開始使用在 C++ 傳統型應用程式中裝載標準 WinRT XAML 控制項的基本案例。 下列各節將介紹您的應用程式可能需要支援的其他案例。
裝載自訂 WinRT XAML 控制項
針對許多案例,您可能需要裝載自訂 WinRT XAML 控制項,其中包含數個要共同作業的個別控制項。 在 C++ 傳統型應用程式中裝載自訂控制項的程序 (您自行定義的控制項或協力廠商所提供的控制項),比裝載標準控制項更複雜,而且需要額外的程式碼。
如需完整的逐步解說,請參閱使用 XAML 裝載 API 在 C++ 傳統型應用程式中裝載自訂 WinRT XAML 控制項。
進階案例
許多裝載 XAML Islands 的傳統型應用程式都必須處理其他案例,才能提供順暢的使用者體驗。 例如,桌面應用程式可能需要在 XAML Islands 中處理鍵盤輸入、在 XAML Islands 與其他 UI 元素之間進行焦點導航,以及版面配置變更。
如需處理這些案例和相關程式碼範例指標的詳細資訊,請參閱 C++ 傳統型應用程式中適用於 XAML Islands 的進階案例。