1. 程式人生 > >WindowsXamlHost:在 WPF 中使用 UWP 控制元件庫中的控制元件

WindowsXamlHost:在 WPF 中使用 UWP 控制元件庫中的控制元件

原文 WindowsXamlHost:在 WPF 中使用 UWP 控制元件庫中的控制元件

在 WindowsXamlHost:在 WPF 中使用 UWP 的控制元件(Windows Community Toolkit) 一文中,我們說到了在 WPF 中引入簡單的 UWP 控制元件以及相關的注意事項。不過,通常更有實際價值的是更復雜的 UWP 控制元件的引入,通常是一整個 Page。

本文將介紹如何在 WPF 專案中引用 UWP 的控制元件庫。


本文內容

建立一個 UWP 控制元件庫

建議專門為你複雜的 UWP 控制元件建立一個 UWP 控制元件庫。在這個控制元件庫中的開發就像普通 UWP 應用一樣。這樣比較容易創建出更復雜的 UWP 控制元件出來,而不會與 WPF 專案產生太多的影響。

建立一個 UWP 控制元件庫
▲ 建立一個 UWP 控制元件庫

選擇 SDK 版本
▲ 選擇 SDK 版本

對 WPF 專案的準備工作

你依然需要閱讀 WindowsXamlHost:在 WPF 中使用 UWP 的控制元件(Windows Community Toolkit)

一文,以便將你的 WPF 專案改造成可以訪問 UWP 型別的專案。

不方便的引入方式

你如果直接讓 WPF 專案新增 UWP 專案的引用,將會得到一個錯誤提示:

不能引用

也就是說並不能直接完成這樣的引用。

也許將來 WPF 專案格式更新或者 Visual Studio 的更新能為我們帶來這樣更直接此引用方式。不過現在來看,還不能如此方便地使用。

編輯 UWP 專案檔案

是的,你需要手工編寫 UWP 的專案檔案。

如果你閱讀過 (1/2) 為了理解 UWP 的啟動流程,我從零開始建立了一個 UWP 程式 這篇文章,或者已經 理解了 C# 專案 csproj 檔案格式的本質和編譯流程

,那麼對這裡 csproj 檔案的編輯應該不會感覺到陌生或者害怕。當然,即便你沒有編輯過或者不理解 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 目錄下

在 WPF 專案中間接引用 UWP 控制元件庫

現在,在 WPF 專案中開啟所有資料夾的顯示,然後將 UWP 專案中生成的檔案新增到 WPF 專案中:

在 WPF 專案中新增 UWP 的控制元件庫
▲ 在 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(); } } } 

參考資料

本文會經常更新,請閱讀原文: https://walterlv.com/post/use-uwp-control-library-in-wpf.html ,以避免陳舊錯誤知識的誤導,同時有更好的閱讀體驗。