.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程序了。

.NET Core学习笔记(2)—— WPF使用UWP Custom Control

创建一个空的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之间的桥梁。

学的快还是忘的快

.NET Core学习笔记(2)—— 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页面。

.NET Core学习笔记(2)—— WPF使用UWP Custom Control

 本篇动手实践的部分较多,还请各位同学下载Sample工程一探究竟。

Github:

https://github.com/manupstairs/WpfHostUwpControlSample

 

IdentityServer4学习笔记汇总(实现传送门在底部)

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享