.NET Core學習筆記(2)—— WPF使用UWP Custom Control

自.NET Core 3.0開始,某軟加入了對WPF的支持。同時對XAML Islands也做了進一步加強。在.NET Core 3.0之前,我們只能在WPF程序中,通過兩種方式有限制地使用Standard UWP Control:

  1. 微軟包裝好的第一方控件,比如InkCanvas,InkToolbar,MediaPlayerElement和MapControl,這些可以直接當成WPF的控件寫到XAML中。

  2. 通過WindowsXamlHost來使用的其他微軟第一方Standard UWP Control,這種方式不支持XAML,需要在cs代碼中進行類型轉換和訂閱事件。

這兩種方式都存在一些問題:

  • 很多UWP      Control和WPF Control雖然同名,但實際仍是不同namespace下的不同class,在傳遞數據時會遇到很大的麻煩,例如設置FontFamily,Brush時會發現其實是兩套同名class,分別屬於System.Windows.Media和Windows.UI.Xaml.Media這兩套namespace。

  • 不支持XAML導致UI的實現受到極大的限制,對Style和Template的使用極不友好。導致在一個WPF程序中,很難將UI界面上同時存在的WPF Control和UWP Control設置成相同的外觀。

而在.NET Core 3.0中,我們終於可以在WPF中嵌入UWP Custom Control。雖仍然是通過WindowsXamlHost來導入Custom Control,但這意味着:

  1. 我們可以創建一個獨立的UWP Control Library。在該Library中,和UWP API的交互將不存在任何障礙。同時通過良好的封裝,將類型轉換的邏輯寫在內部,對外僅暴露WPF類型的情況下,可以有效的避免UWP Control和WPF Control同名的問題。在外部調用的WPF程序看來,這就是一個純的WPF Control。

  2. 因爲Style和Template不再受到限制,對UI的控制力完全釋放,我們甚至可以將整個頁面放到UWP      Custom Control中,製作一個完全使用UWP Control的exe程序。

下面我們就來看看如何具體實現:

首先我們需要安裝.NET Core 3.0,在此基礎之上我們纔可以創建基於.NET Core的WPF程序了。

創建一個空的WPF工程後,在NuGet中搜索Microsoft.Toolkit.Wpf.UI.XamlHost並進行安裝。這個Package將提供WindowsXamlHost對象供我們導入UWP Custom Control。完成後,記得將WPF工程的類型,從默認的Any CPU改成x64或者x86。這是因爲UWP工程不支持Any CPU。

接下來創建的第二個工程,是一個空的UWP工程,UWP的版本確保是1903或者更新。通過NuGet安裝Microsoft.Toolkit.Win32.UI.XamlApplication。這個空的UWP工程將成爲WPF程序和UWP Custom Control之間的橋樑。

對於這個空的UWP工程,我們僅需保留App.xml文件,將App的繼承關係改爲繼承自XamlApplication對象。

<xaml:XamlApplication
    x:Class="MyUWPApp.App"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    xmlns:xaml="using:Microsoft.Toolkit.Win32.UI.XamlHost"
    xmlns:local="using:MyUWPApp">
</xaml:XamlApplication>

 App.xaml.cs中,也僅保留構造函數即可。

    sealed partial class App : Microsoft.Toolkit.Win32.UI.XamlHost.XamlApplication
    {
        /// <summary>
        /// Initializes the singleton application object.  This is the first line of authored code
        /// executed, and as such is the logical equivalent of main() or WinMain().
        /// </summary>
        public App()
        {
            this.Initialize();
        }
    }

接着將MainPage.xaml文件整個刪除,編譯通過後,在WPF工程裏添加對MyUWPApp的reference,這個中間人的工程就準備完畢了。

接下來讓我們創建真正的UWP Custom Control Library。首先選擇Class Library(Universal Windows),創建完成後,右鍵工程文件選擇Unload再選擇Edit,在工程文件的最下方,添加下述配置項:

  <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
       Other similar extension points exist, see Microsoft.Common.targets.
  <Target Name="BeforeBuild">
  </Target>
  <Target Name="AfterBuild">
  </Target>
  -->
  <PropertyGroup>
    <EnableTypeInfoReflection>false</EnableTypeInfoReflection>
    <EnableXBindDiagnostics>false</EnableXBindDiagnostics>
  </PropertyGroup>
</Project>

保存後Reload工程。完成上述操作後,再新建幾個簡單的UWP Custome Control。之後需要在WPF和MyUWPApp這兩個工程中,分別添加對UWPClassLibrary工程的reference。這些reference的添加都是必須的,缺少了其中的任何一項,都會導致最終Host UWP Control失敗。

此時讓我們返回到WPF工程中,開始使用UWPClassLibrary中剛剛創建的UWP Custom Control。

    <Grid>
        <xaml:WindowsXamlHost InitialTypeName="UWPClassLibrary.SplitViewDemo"  ChildChanged="WindowsXamlHost_ChildChanged_1" />
    </Grid>

和之前使用微軟第一方Control較爲類似,也是通過WindowsXamlHost 節點指定類型,以及在ChildChanged事件來進行類型轉換獲取對象實例。

        private void WindowsXamlHost_ChildChanged_1(object sender, EventArgs e)
        {
            global::Microsoft.Toolkit.Wpf.UI.XamlHost.WindowsXamlHost windowsXamlHost =
                sender as global::Microsoft.Toolkit.Wpf.UI.XamlHost.WindowsXamlHost;
            global::UWPClassLibrary.SplitViewDemo userControl =
                windowsXamlHost.GetUwpInternalObject() as global::UWPClassLibrary.SplitViewDemo;
  
            if (userControl != null)
            {
                userControl.IconList = new ObservableCollection<NavItem>
            {
                new NavItem { Symbol= Symbol.Save},
                new NavItem { Symbol= Symbol.Scan},
                new NavItem { Symbol= Symbol.Share},
                new NavItem { Symbol= Symbol.Stop},
                new NavItem { Symbol= Symbol.Video},
                new NavItem { Symbol= Symbol.Volume},
            };
            }
        }

在我們的Sample工程中,我將名爲SplitViewDemo的UserControl整個丟到了WPF工程的Page中。運行後我們可以發現,這看起來就像是套了WPF外殼的UWP頁面。

 本篇動手實踐的部分較多,還請各位同學下載Sample工程一探究竟。

Github:

https://github.com/manupstairs/WpfHostUwpControlSample

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章