WindowsXamlHost:在 WPF 中使用 UWP 控制元件庫中的控制元件
原文 WindowsXamlHost:在 WPF 中使用 UWP 控制元件庫中的控制元件
在 WindowsXamlHost:在 WPF 中使用 UWP 的控制元件(Windows Community Toolkit) 一文中,我們說到了在 WPF 中引入簡單的 UWP 控制元件以及相關的注意事項。不過,通常更有實際價值的是更復雜的 UWP 控制元件的引入,通常是一整個 Page。
本文將介紹如何在 WPF 專案中引用 UWP 的控制元件庫。
本文內容
- 建立一個 UWP 控制元件庫
- 對 WPF 專案的準備工作
- 不方便的引入方式
- 重新載入專案並編譯
- 在 WPF 專案中間接引用 UWP 控制元件庫
- 在 WPF 專案中使用 UWP 控制元件庫中的控制元件
建立一個 UWP 控制元件庫
建議專門為你複雜的 UWP 控制元件建立一個 UWP 控制元件庫。在這個控制元件庫中的開發就像普通 UWP 應用一樣。這樣比較容易創建出更復雜的 UWP 控制元件出來,而不會與 WPF 專案產生太多的影響。
▲ 建立一個 UWP 控制元件庫
▲ 選擇 SDK 版本
對 WPF 專案的準備工作
你依然需要閱讀 WindowsXamlHost:在 WPF 中使用 UWP 的控制元件(Windows Community Toolkit)
不方便的引入方式
你如果直接讓 WPF 專案新增 UWP 專案的引用,將會得到一個錯誤提示:
也就是說並不能直接完成這樣的引用。
也許將來 WPF 專案格式更新或者 Visual Studio 的更新能為我們帶來這樣更直接此引用方式。不過現在來看,還不能如此方便地使用。
編輯 UWP 專案檔案
是的,你需要手工編寫 UWP 的專案檔案。
如果你閱讀過 (1/2) 為了理解 UWP 的啟動流程,我從零開始建立了一個 UWP 程式 這篇文章,或者已經 理解了 C# 專案 csproj 檔案格式的本質和編譯流程
現在,右擊解除安裝專案,再右擊編輯專案檔案:
▲ 編輯專案檔案
找到 Import
targets 的哪一行,你需要在那一行前面的任意位置新增以下特別標註為新增的幾行:
++ <PropertyGroup>
++ <EnableTypeInfoReflection>false</EnableTypeInfoReflection>
++ <EnableXBindDiagnostics>false</EnableXBindDiagnostics>
++ </PropertyGroup>
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WindowsXaml\v$(VisualStudioVersion)\Microsoft.Windows.UI.Xaml.CSharp.targets" />
隨後,還要在以上 targets 之後再新增以下程式碼:
<PropertyGroup>
<!-- 這裡需要填寫你的 WPF 專案的路徑 -->
<HostFrameworkProjectFolder>$(ProjectDir)..\Whitman.Wpf</HostFrameworkProjectFolder>
<ObjPath>obj\$(Platform)\$(Configuration)\</ObjPath> </PropertyGroup> <PropertyGroup Condition=" '$(Platform)' == 'AnyCPU' "> <ObjPath>obj\$(Configuration)\</ObjPath> </PropertyGroup> <PropertyGroup> <!-- 把此專案的輸出檔案都拷貝到 WPF 專案的生成路徑下 --> <PostBuildEvent> md $(HostFrameworkProjectFolder)\$(ProjectName) md $(HostFrameworkProjectFolder)\bin\$(Configuration)\$(ProjectName) copy $(TargetDir)*.xbf $(HostFrameworkProjectFolder)\bin\$(Configuration)\$(ProjectName) copy $(ProjectDir)*.xaml $(HostFrameworkProjectFolder)\bin\$(Configuration)\$(ProjectName) copy $(ProjectDir)*.xaml.cs $(HostFrameworkProjectFolder)\$(ProjectName) copy $(ProjectDir)$(ObjPath)*.g.* $(HostFrameworkProjectFolder)\$(ProjectName) </PostBuildEvent> </PropertyGroup>
需要注意:
- 一定要在 targets 之後新增這些程式碼,因為
$(TargetDir)
、$(ProjectName)
等屬性是在那裡的 targets 執行完後才生成的。 - 你的 UWP 專案中需要有 xaml,比如可以新增一個 MainPage.xaml 和 MainPage.xaml.cs,不然編譯的時候可能會出現錯誤。
重新載入專案並編譯
現在,重新載入那個 UWP 控制元件庫,將其編譯,以便將 UWP 專案的生成檔案複製到 WPF 目錄下。
▲ 生成的檔案已複製到 WPF 目錄下
在 WPF 專案中間接引用 UWP 控制元件庫
現在,在 WPF 專案中開啟所有資料夾的顯示,然後將 UWP 專案中生成的檔案新增到 WPF 專案中:
▲ 在 WPF 的專案中新增 UWP 的控制元件庫
為了能夠在每次編譯 WPF 專案的時候確保 UWP 專案先編譯,需要為 WPF 專案設定專案依賴。在依賴對話方塊中將 UWP 專案設為依賴。
▲ 新增專案依賴
現在,編譯 WPF 專案的時候,會將 UWP 專案編譯後的原始碼也一起編譯到 WPF 專案中;相當於間接使用了 UWP 的控制元件庫。
特別的,如果你的專案被 git 進行版本管理,你可能需要忽略 UWP 控制元件庫專案中的檔案。方法是在 WPF 專案內生成的 UWP 資料夾下新增一個 .gitignore 檔案,填寫所有內容忽略:
*.*
但記得需要額外通過 git add ./Whitman.Wpf/Whitman.Uwp/.gitignore
把這個檔案新增到版本管理中,不然其他人不會生效。
在 WPF 專案中使用 UWP 控制元件庫中的控制元件
這時,在 WindowsXamlHost
中就可以新增 UWP 控制元件庫中的 MainPage 了。
<XamlHost:WindowsXamlHost InitialTypeName="Walterlv.Whitman.Universal.MainPage" />
於是,你可以在區域性獲得 UWP 完整 Page 的支援。或者你整個介面都是用 UWP 開發都沒問題,並且還能獲得 .NET Framework 的完全訪問支援。(當然,未來一定是 .NET Core。)
▲ 執行後的效果
可以使用 UWP 的 Page,並且也能彈出 UWP 的 MessageDialog
。
而 MainPage 就是普通的 UWP MainPage:
<Page
x:Class="Walterlv.Whitman.Universal.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="using:Walterlv.Whitman.Universal" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" Background="{ThemeResource ApplicationPageBackgroundThemeBrush}"> <StackPanel Width="400" VerticalAlignment="Center"> <TextBlock> <Run Text="歡迎訪問 呂毅的部落格" /> <LineBreak /> <Run Text="https://walterlv.com" /> </TextBlock> <Button Content="Click" Click="DemoButton_Click" /> </StackPanel> </Page>
using System;
using Windows.UI.Popups; using Windows.UI.Xaml; using Windows.UI.Xaml.Controls; namespace Walterlv.Whitman.Universal { public sealed partial class MainPage : Page { public MainPage() => InitializeComponent(); private async void DemoButton_Click(object sender, RoutedEventArgs e) { var button = (Button) sender; await new MessageDialog("UWP 的訊息框,在 WPF 的視窗中。", "walterlv").ShowAsync(); } } }
參考資料
- 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-control-library-in-wpf.html ,以避免陳舊錯誤知識的誤導,同時有更好的閱讀體驗。