WindowsXamlHost:在 WPF 中使用 UWP 的控制元件(Windows Community Toolkit)
原文 WindowsXamlHost:在 WPF 中使用 UWP 的控制元件(Windows Community Toolkit)
Windows Community Toolkit 再次更新到 5.0。以前可以在 WPF 中使用有限的 UWP 控制元件,而現在有了 WindowsXamlHost,則可以使用更多 UWP 原生控制元件了。
關於 Windows Community Toolkit 早期版本的 Xaml Bridge,可以參見:
本文內容
- 安裝 NuGet 包
- 配置 WPF 專案能訪問 UWP 的型別
- 開始在 WPF 中使用 UWP 的控制元件
- 可以忽略的錯誤
- 最終效果
- 值得注意的地方
- 關於 DPI 適配
- 更復雜的 UWP 控制元件嵌入
安裝 NuGet 包
你需要做的第一步,是在你的 WPF 專案中安裝 Microsoft.Toolkit.Wpf.UI.XamlHost。建議直接在 專案的 NuGet 管理器中搜索並安裝。
配置 WPF 專案能訪問 UWP 的型別
因為我們即將開始使用到 UWP 中的控制元件型別,所以需要配置專案能夠訪問到 Windows Runtime 的型別。
▲ 新增引用
你需要在你的 WPF 專案中新增以下 6 個引用才能訪問 UWP 的型別:
- C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework.NETCore\v4.5
- 引用 System.Runtime.WindowsRuntime
- 引用 System.Runtime.WindowsRuntime.UI.Xaml
- 引用 System.Runtime.InteropServices.WindowsRuntime
- C:\Program Files (x86)\Windows Kits\10\UnionMetadata\Facade
- 引用 Windows.winmd
- C:\Program Files (x86)\Windows Kits\10\References\
- 在此目錄下選擇你的 SDK 版本(如 16299,17763 等)
- Windows.Foundation.UniversalApiContract\
- 在此目錄下選擇你的 API 版本(如 4.0.0.0)
- 引用 Windows.Foundation.UniversalApiContract.winmd
- 在此目錄下選擇你的 API 版本(如 4.0.0.0)
- Windows.Foundation.FoundationContract
- 在此目錄下選擇你的 API 版本(如 3.0.0.0)
- 引用 Windows.Foundation.FoundationContract.winmd
- 在此目錄下選擇你的 API 版本(如 3.0.0.0)
- Windows.Foundation.UniversalApiContract\
- 在此目錄下選擇你的 SDK 版本(如 16299,17763 等)
在你新增完這些引用之後,還需要選中這些引用,右擊屬性,把所有的 “複製到本地” 選項設定為 “否”。
▲ 新增 Windows Runtime 的 .NET Framework 型別引用
▲ 新增 Windows.WinMD 的引用
▲ 在新增引用時注意選擇 SDK 的版本號
▲ 新增 Windows.Foundation.UniversalApiContract.winmd
▲ 新增 Windows.Foundation.FoundationContract.winmd
開始在 WPF 中使用 UWP 的控制元件
你可以像使用普通 WPF 控制元件一樣將 WindowsXamlHost 新增到你的 WPF 介面中:
- 拖拽到介面設計器中
- 拖拽到 XAML 程式碼行中
- 直接在 XAML 程式碼中寫
▲ 新增 WindowsXamlHost 控制元件
接著,指定 InitialTypeName
屬性為 UWP 中的控制元件的名稱(帶名稱空間)。這樣,當 WindowsXamlHost 初始化的時候,也會初始化一個 UWP 的控制元件。
這裡為了簡單,我初始化一個 UWP 的按鈕。但必須得為 UWP 的按鈕進行一些初始化,所以我監聽了 ChangedChanged
事件:
<XamlHost:WindowsXamlHost Grid.Column="1"
InitialTypeName="Windows.UI.Xaml.Controls.Button" ChildChanged="WindowsXamlHost_ChildChanged" />
private void WindowsXamlHost_ChildChanged(object sender, EventArgs e) { var host = (WindowsXamlHost) sender; var button = (Windows.UI.Xaml.Controls.Button) host.Child; button.Width = 120; button.Height = 40; button.Content = "walterlv.com"; button.Click += UwpButton_Click; } private void UwpButton_Click(object sender, RoutedEventArgs e) { }
可以忽略的錯誤
在啟動的時候,你可能會遇到一些異常。比如下面這個:
因為我們不是原生的 UWP,而是 Host 在 WPF 中的 UWP 控制元件,所以會沒有 Application
。這在 UWP 控制元件初始化內部已經 catch
了,所以你可以忽略。
最終效果
當將程式跑起來之後,你就能看到 WPF 視窗中的 UWP 控制元件了。
值得注意的地方
- 目前 WindowsXamlHost 還不夠穩定,會出現一些閃退
- 這點就需要為 WindowsCommunityToolkit 貢獻 Issues 或程式碼了
- Host 的 UWP 控制元件是一個新的 HwndSource,這相當於 UWP 的控制元件是通過子視窗的形式與 WPF 視窗放在一起的
- 於是,只能指定一個矩形區域完全屬於 UWP,在這個區域 WPF 控制元件無法與其獲得互動或渲染疊加
關於 DPI 適配
為了讓 UWP 控制元件能夠在 WPF 視窗中獲得正確的 Per-Monitor 的 DPI 適配效果,你需要設定為 PerMonitorV2 的 DPI 感知級別。
在 PerMonitorV2 的 DPI 感知級別下,UWP 控制元件能夠正常獲得 DPI 縮放。
在 100% DPI 的螢幕下:
在 150% DPI 的螢幕下:
而如果只是指定為 PerMonitor,那麼切換 DPI 或者切換螢幕的時候,只有 WPF 部分會縮放,而 UWP 部分不會變化。
關於 PerMonitorV2 和 PerMonitor 的理解和區別,可以參見:
關於如何在 WPF 下開啟 PerMonitorV2 級別的 DPI 感知可以參見:
更復雜的 UWP 控制元件嵌入
如果希望將更多的 WPF 視窗內的 UI 部分交給 UWP 來做,那麼就不能只是僅僅初始化一個 Button
就完了。
你需要引入一個 UWP 控制元件庫。閱讀以下文章瞭解更多:
參考資料
- WindowsXAMLHost control - Windows Community Toolkit - Microsoft Docs
- Enhance your desktop application for Windows 10 - UWP app developer - Microsoft Docs
本文會經常更新,請閱讀原文: https://walterlv.com/post/use-uwp-controls-in-wpf.html ,以避免陳舊錯誤知識的誤導,同時有更好的閱讀體驗。