诗林 发表于 2024-6-23 10:54:59

dotnet 融合 Avalonia 和 UNO 框架

现在在 .NET 系列里面,势头比较猛的 UI 框架中,就包括了 Avalonia 和 UNO 框架。本文将告诉各人如何尝试在一个办理方案里面融合 Avalonia 和 UNO 两个框架,即在一个进程里面跑起来两个框架
开始之前先聊会背景故事
我比较看好 Avalonia 的现在和 UNO 的将来。但是我不怎么想在 Avalonia 的基础上造基础库和办法。我大概是在 2017 年的时间就参与了 Avalonia 的开辟,但是随着更深入的投入发现了 Avalonia 团队的一些问题,那会感觉到 Avalonia 更像是一个玩具、一个实验场,而不是一个可产物化的应用。核心原因在于有一些意见上没能和我达成同等。一个框架开辟必要比较全面的能力和知识,有一些知识属于特定范畴的。但是 Avalonia 团队里面缺乏这部分知识,且很多时间都是拍脑袋按照自己想法举行实现的。这就导致了一些专业的模块实现过于奇怪。其输入法、文本相关、触摸相关部分实现都比较糟心。界面布局方式,以及一些基础实现和我预期相差较大。再加上版本之间的 API 稳定性和行为稳定性,导致了我不想在 Avalonia 的基础上举行投入基础库的制造和基础办法的搭建
好在 2023 的下半年(正确来说是3月,但是刚开始没有什么影响),进了一位 CEO 到 Avalonia 团队,在这个期间给 Avalonia 带来极大的提拔,直接从一个玩具级提拔到产物级。这个过程中 Avalonia 做了相当多的工作,包括举行了大规模的重构,大量基础办法的建设,优化了非常多的开辟调试的能力。整体开辟 Avalonia 起来也黑白常舒坦,且有了支持大型项目的能力。得益于 Avalonia 非常长的开源时间作为底蕴,从 2013 年开源至今,在 Avalonia 框架里面积累了大量的跨平台履历,特别是在 Linux 的桌面端应用上的履历,举行了非常多的适配。再加上 2023 的下半年进了 Mike James 作为 Avalonia 的 CEO 角色,让 Avalonia 有了非常多的资源投入,以及拉动了非常多相关方的支持,使得 Avalonia 迎来一大波激进的优化。优化方面包括了框架底层到上层 API 的重构,也包括了拉来了 JetBrains 的 ReSharper 和 Rider 的官方支持使得开辟调试等各种方面的有了非常大的优化
可以这么说,整个 Avalonia 的开辟体验,从 2023 的下半年可以和之前作为一个巨大的划分点。让我打分的话,之前是不合格,现在是 90 分
但是 Mike James 在 2023 的 11 月 跑路了,这就使得从 2024 的上半年开始,整个 Avalonia 的紊乱程度又上来了。好在现在 Mike James 又回到 Avalonia 团队了,期待后续 Avalonia 团队的进步
以下是从 https://theorg.com/org/avalonia-ui/org-chart/mike-james 里面拷贝的 Mike James 简介,可以看到他是很厉害的且有履历的
Mike James has a diverse work experience in the technology industry. Mike is currently serving as the Chief Executive Officer of Avalonia UI since March 2023. Prior to that, they worked at Microsoft from 2016 to 2023, where they held various roles including Senior Developer Advocate, Technical Solutions Professional, and Program Manager II. From 2013 to 2017, Mike worked at Xamarin as a Developer Evangelist/Advocate and a Customer Support Engineer. Mike started their career at Pharos Architectural Controls Ltd in 2010 as a Development Support Technician.
这也就是为什么我看好 Avalonia 的现在的原因。当然了 Mike James 是一个原因,客套来说其整个团队也都功不可没。那接下来继续聊一下 UNO 框架
整个 UNO 框架起初是创建在 WinUI 的侧边的,即在现有的 WinUI 或 UWP 应用里面,使用 UNO 框架将其构建出跨平台的版本。如许做的策略是 UNO 框架可以复用 UWP 的基础办法和 API 设计。从一开始上就规避了 Avalonia 里面紊乱的 API 设计和基础办法。但是缺点也很明显,就是 WinUI 的 API 设计比 Win32 先辈差太多了,且 UWP 也砍掉了大量的 WPF 能力,导致了 UNO 被 WinUI 所拖累。再加上 UNO 开源时间还短,距今仅有 6 年时间,再加上 UNO 同时在啃食全平台,即移动端 和 WASM 和桌面端,导致了美满程度不如 Avalonia 高
但 UNO 的优势在于有强有力的控制管理,这和以前(特指 2023 之前)的容易受到社区投毒的 Avalonia 有着巨大的不同,其交付能力有所保证。其次是 UNO 的整体开辟团队投入和卷的程度看起来比 Avalonia 更大得多。最后是使用了 WinUI 的 API 组织方式举行兜底,以及参考了 WinUI3 的设计,确保了很多专业性模块上的实现正确性。最后一点是和 Avalonia 策略上的差别,在 UNO 上是宁可不实现也尽量不给出知识性错误的实现方式,而 Avalonia 则是别人有我就得有,不管是否不平水土。再加上 WinUI 和 MAUI 团队对 UNO 的帮衬,让 UNO 的整体技术更加全面。这就是我比较看好 UNO 的将来的原因
那如何现在我就必要开辟呢?我敢不敢全用 UNO 呢?如果是桌面端的话,不敢,由于现在的 UNO 在桌面端完全不敷 Avalonia 打的。选 Avalonia 呢,但是我的基础库和基础办法照旧必要造的,一旦选 Avalonia 就意味着我有大量的测试实验必要做,去测试 Avalonia 的各种行为,且可能在下个 Avalonia 版本发布之后,我的这些测试实验和基础库就全都白干了,由于 Avalonia 就举行了不兼容的修改变动了。嗯…在 Avalonia 的 11.0 和 11.1 已经干了这件事了。既然我看好 UNO 的将来,那不如基础库就在 UNO 的基础上造咯。纵然我说 UNO 在桌面端完全不敷 Avalonia 打的,但是作为基础库所需的基础能力,照旧可以或许提供的
于是我就选择了上层应用使用 Avalonia 做,底层一些基础办法使用 UNO 做。我就有了两个框架在一个应用里面,于是就有了本文的 融合 Avalonia 和 UNO 框架到一个办理方案里面。以下是 Avalonia 对此的评价:
Avalonia, unlike Uno, isn't a direct reimplementation of any existing framework but a modern, cross-platform toolkit inspired by WPF. Avalonia focuses on rendering the entire UI independently, akin to Flutter, ensuring consistent, high-quality visuals on all platforms. In contrast, Uno aims to replicate the UWP & WinUI API across platforms, utilising native primitives similar to MAUI. This makes Avalonia ideal for creating a uniform, flexible UI design across every platform. We advise developers to try both technologies when considering Avalonia vs Uno Platform and decide which they prefer.
我就听了他 We advise developers to try both technologies 半句话,好的,那就尝试两个框架咯
为什么不是合并 Avalonia 和 UNO 框架到一个项目里面?这个想法太可怕了,这两个框架都是举行了大量且深度的黑科技研发的,可以或许在一个办理方案里面共存能活就好了
以下是我给出的最简的让 Avalonia 和 UNO 框架跑在一个进程上的方法
分别新建 Avalonia 和 UNO 框架两个项目的最简模式,此中 Avalonia 框架定名为 AvaloniaIDemo 项目,将 UNO 框架定名为 UnoDemo 框架。我特别推荐各人先拿空项目举行测试,玩明白了再修改复杂的项目
最简跑起来的方法就是让 UnoDemo 项目引用 AvaloniaIDemo 项目,这时间看起来构建什么的都没有问题。为了测试将 Avalonia 跑起来,修改 UnoDemo 项目的 MainPage.xaml 文件,添加一个按钮,点击这个按钮可以将 Avalonia 框架跑起来,代码如下
<Page x:
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:UnoDemo"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel
      HorizontalAlignment="Center"
      VerticalAlignment="Center">
    <TextBlock AutomationProperties.AutomationId="HelloTextBlock"
          Text="Hello Uno Platform"
          HorizontalAlignment="Center" />
    <Button x:Name="RunAvaloniaButton" HorizontalAlignment="Center" Content="Run Avalonia" Click="RunAvaloniaButton_OnClick"/>
</StackPanel>
</Page>背景代码实现如下
    private void RunAvaloniaButton_OnClick(object sender, RoutedEventArgs e)    {      // Create the new Thread to run the Avalonia<Page x:
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:UnoDemo"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel
      HorizontalAlignment="Center"
      VerticalAlignment="Center">
    <TextBlock AutomationProperties.AutomationId="HelloTextBlock"
          Text="Hello Uno Platform"
          HorizontalAlignment="Center" />
    <Button x:Name="RunAvaloniaButton" HorizontalAlignment="Center" Content="Run Avalonia" Click="RunAvaloniaButton_OnClick"/>
</StackPanel>
</Page>   var thread = new Thread(() =>      {<Page x:
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:UnoDemo"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel
      HorizontalAlignment="Center"
      VerticalAlignment="Center">
    <TextBlock AutomationProperties.AutomationId="HelloTextBlock"
          Text="Hello Uno Platform"
          HorizontalAlignment="Center" />
    <Button x:Name="RunAvaloniaButton" HorizontalAlignment="Center" Content="Run Avalonia" Click="RunAvaloniaButton_OnClick"/>
</StackPanel>
</Page>AvaloniaIDemo.Program.Main([]);      })      {<Page x:
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:UnoDemo"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel
      HorizontalAlignment="Center"
      VerticalAlignment="Center">
    <TextBlock AutomationProperties.AutomationId="HelloTextBlock"
          Text="Hello Uno Platform"
          HorizontalAlignment="Center" />
    <Button x:Name="RunAvaloniaButton" HorizontalAlignment="Center" Content="Run Avalonia" Click="RunAvaloniaButton_OnClick"/>
</StackPanel>
</Page>IsBackground = true,<Page x:
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:UnoDemo"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel
      HorizontalAlignment="Center"
      VerticalAlignment="Center">
    <TextBlock AutomationProperties.AutomationId="HelloTextBlock"
          Text="Hello Uno Platform"
          HorizontalAlignment="Center" />
    <Button x:Name="RunAvaloniaButton" HorizontalAlignment="Center" Content="Run Avalonia" Click="RunAvaloniaButton_OnClick"/>
</StackPanel>
</Page>Name = "Avalonia main thread"      };      if (OperatingSystem.IsWindows())      {<Page x:
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:UnoDemo"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel
      HorizontalAlignment="Center"
      VerticalAlignment="Center">
    <TextBlock AutomationProperties.AutomationId="HelloTextBlock"
          Text="Hello Uno Platform"
          HorizontalAlignment="Center" />
    <Button x:Name="RunAvaloniaButton" HorizontalAlignment="Center" Content="Run Avalonia" Click="RunAvaloniaButton_OnClick"/>
</StackPanel>
</Page>thread.SetApartmentState(ApartmentState.STA);      }      thread.Start();    }这里必要新建一个背景线程给 Avalonia 跑,由于 Avalonia 一跑就会卡住线程,只有在 Avalonia 应用退出时才会退出卡住线程逻辑
额外说明为什么不用 Task 的方式跑,而是选择 Thread 的原因,这是由于 Task 默认走线程池,线程池可不开森你拿一个线程跑长时间的任务,如许会占用线程池的资源。对于此业务情况下,必要长时间运行的,那就是自己开 Thread 更好
以上就是最基础的实现方法了
本文以上代码放在 github 和 gitee 上,可以使用如下命令行拉取代码
先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码
git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 6ef2308fd744d276c4db076ac58efaad7b0d1c25以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源举行拉取代码
git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 6ef2308fd744d276c4db076ac58efaad7b0d1c25获取代码之后,进入 AvaloniaIDemo/WawjejokeniDejeyaibeweji 文件夹,即可获取到源代码
可以试试拉取我的代码,打开 AvaloniaIDemo\WawjejokeniDejeyaibeweji\AvaloniaIDemo.sln 文件,然后切换 UnoDemo 作为启动项目,选择 UnoDemo(Desktop) 而不是 UnoDemo(WinAppSDK Packaged) 跑起来,再试试点击按钮看看是否可以或许弹出 AvaloniaIDemo 的窗口
此时跑起来的应用是 Avalonia+WPF+Uno 三个框架的,哈哈,为什么这里会加上 WPF 呢?这是由于 UNO 在 Windows 的底层就是 WPF 框架承接。而 Avalonia 是自己对接 Win32 层,没有中间商
可以看到本文的这个方式做的是比较浅的融合,窗口级相互引用而已。更深层次的融合现在可行性不高,欢迎各人自行探索
以下是我的更多踩坑履历
找不到 SDK 项目添加不上来

如果一开始新建的 sln 文件是对 Avalonia 项目的,那么将可以在添加现有 UNO 项目时,发现 VisualStudio 不给添加,提示报错信息如下
找不到指定的 SDK "Uno.Sdk" 项目无法添加
这个原因是在 sln 文件雷同的文件夹下找不到包含 Uno.Sdk 定义的 global.json 文件。只需在 sln 文件雷同的文件夹下放一个 global.json 文件,里面的内容代码大概如下
{
// To update the version of Uno please update the version of the Uno.Sdk here. See https://aka.platform.uno/upgrade-uno-packages for more information.
"msbuild-sdks":
{
    "Uno.Sdk": "5.2.161"
}
}以上的 5.2.161 版本号,还请修改为你创建 UNO 项目时的选用版本号。或者直接将 UNO 项目的 global.json 文件拷贝过去也可以
这是由于在此版本时,新建的 UNO 项目的 csproj 项目文件里使用了 UNO 自己制作的 Uno.Sdk 而不是 Microsoft.NET.Sdk 框架
以下为 UNO 项目的 csproj 项目文件的示例代码
<Project Sdk="Uno.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
    ... 忽略其他代码
</Project>
</Project>以下为其他控制台或 Avalonia 项目的 csproj 项目文件的示例代码
<Project Sdk="Uno.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
    ... 忽略其他代码
</Project>
</Project>可以看到 Sdk 属性的不同
无法在 Avalonia 项目引用 UNO 项目

为什么在本文例子里面是使用 UNO 项目引用 Avalonia 项目,而不是反过来呢?这是由于在 UNO 的 5.2 版本里面,自创了名为 netx.xx-desktop 的框架。从 dotnet 设计上说,自己创建框架也是可行的,究竟 dotnet 里面就有了 netx.xx-windows 等框架,用于区分平台
在 UNO 里,确实使用 netx.xx-desktop 框架可以让内部开辟更加便利,实现桌面端的跨平台和移动端等的区分
但是这也导致了与其他现有办法对接时间的难点。现在 Avalonia 依然使用的是纯 dotnet 项目,这让 Avalonia 的构建非常简单且稳定。各人都知道,对构建过程更多的定制就一般意味着会有更多诡异的问题,现在的 UNO 就是如许。整体的构建不仅被 WinUI 拖累,还会有自己创建的框架的坑。纵然 UNO 团队有专职的测试人员也架不住开辟者复杂的开辟环境投毒
如果让 Avalonia 项目引用 UNO 项目,将会构建失败,错误信息如下
error NU1201: 项目 UnoDemo 与 net8.0 (.NETCoreApp,Version=v8.0) 不兼容其原因就是 UNO 使用的是 net8.0-desktop 框架,而 Avalonia 项目是 net8.0 框架的。从 dotnet 的 SDK 设计约束上就是 net8.0-desktop 框架范围比 net8.0 框架更大,不能让更小范围的框架引用更大的范围,这就是失败的原因
发布 Linux 平台失败

发布 linux 平台时,必要先在 Avalonia 项目里面举行一次发布,发布参数必要和 UNO 项目的雷同。如在 AvaloniaIDemo 里面,选用 Release 加 linux-x64 的独立发布方式举行发布,再在 UNO 项目也选用 Release 加 linux-x64 的独立发布方式举行发布,如此才能发布乐成
否则如果只是立刻在 UNO 项目里发布,则可能遇到 未能找到元数据文件 错误,失败信息大概如下
未能找到元数据文件“C:\lindexi\Code\AvaloniaIDemo\WawjejokeniDejeyaibeweji\AvaloniaIDemo\obj\Release\net8.0\linux-x64\ref\AvaloniaIDemo.dll”这个原因大概是 Avalonia 也对发布做了些科技,导致的不兼容问题。更细节我没有继续研究
经过我的测试,如此方式发布之后,可以在 Ubuntu 和 UOS 两个 Linux 系统上运行,且工作符合预期
让 Avalonia 依赖 net8.0 的 Uno 项目

由于 Uno 不仅可以跑 net8.0-desktop 框架,作为全平台支持的 UI 框架,天生也就支持单跑 net8.0 框架。尝试修改 UNO 的 csproj 项目文件为如下代码
<Page x:
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:UnoDemo"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel
      HorizontalAlignment="Center"
      VerticalAlignment="Center">
    <TextBlock AutomationProperties.AutomationId="HelloTextBlock"
          Text="Hello Uno Platform"
          HorizontalAlignment="Center" />
    <Button x:Name="RunAvaloniaButton" HorizontalAlignment="Center" Content="Run Avalonia" Click="RunAvaloniaButton_OnClick"/>
</StackPanel>
</Page>net8.0-windows10.0.19041;      net8.0-desktop;      net8.0;    <Project Sdk="Uno.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
    ... 忽略其他代码
</Project>
</Project>此时就满足了给 Avalonia 引用的基础条件了,然而此时却会发现 Avalonia 常常无法创建生成代码,这是由于 Avalonia 所做的黑科技刚好和 Uno 所作的冲突,从而导致 Avalonia 无法乐成从 axaml 生成代码
同时也存在了许多范例冲突,进一步导致了项目难以构建。如我的以下变动: https://github.com/lindexi/lindexi_gd/commit/f7032fc9a952a073d1e4cdb5d81955e38019ac3c 将会难以举行构建
纵然通过仅引用程序集办理了引用问题,也会面临着运行不起来的问题。这是由于 Uno 只有在 desktop 下才拷贝真正的桌面运行时依赖,如 Uno.UI.Runtime.Skia.Wpf.dll 和 Uno.UI.Runtime.Skia.X11.dll 文件,缺少了这些文件的 Uno 程序集是无法正常运行的
且如果你的 IDE 是 Rider 的话,更会出现问题。在 Rider 里面,只会构建所需的框架,纵然只对 UnoDemo 构建 net8.0 框架,而无视 net8.0-desktop 框架。尽管 Rider 这个是为了优化构建速度,但是也带来了更多问题,如现在就无法通过拷贝 net8.0-desktop 框架的内容到输出路径举行替换从而办理运行的问题
办理 Rider 的不构建的方法是接纳办理方案的依赖的方式
范例冲突

由于 Avalonia 存在大量的范例和 UNO/WinUI 雷同,这将会导致如此引用将会很难写代码
比如写一个 Thickness 都可能遇到以下错误信息
MainPage.xaml.cs(15,9,15,18): error CS0104: “Thickness”是“Avalonia.Thickness”和“Microsoft.UI.Xaml.Thickness”之间的不明确的引用想要更好的办理是再新建一个作为入口的程序集,这个程序集依然是 UNO 框架的,分别引用 AvaloniaIDemo 和 UnoDemo 项目,只在此入口程序集做启动和实现对接,其他的事情都不要做
为了更好的实现对接,那一般还必要一个纯 dotnet 项目,这个项目是 API 定义项目,用于让互相不引用的 AvaloniaIDemo 和 UnoDemo 通过此 API 定义项目举行抽象对接
如此各人也可以看到通过这个方式开辟具备肯定的复杂性
接下来我将告诉各人这个方法
新入口程序集

看起来再新建一个程序集作为入口程序集也不错,此方式只是搭建稍微有点复杂而已,但可以或许确保 Avalonia 和 Uno 项目更具独立性
新建一个名为 AppDemo 的控制台项目,再新建一个名为 LibDemo 的控制台项目。断开 UnoDemo 和 AvaloniaIDemo 的接洽。让 UnoDemo 和 AvaloniaIDemo 项目都引用 LibDemo 项目。让 AppDemo 同时引用 UnoDemo 和 AvaloniaIDemo 项目
完成引用之后的项目引用关系如下图
https://img2024.cnblogs.com/blog/1080237/202406/1080237-20240623071511188-2144564221.jpg
为了让 AppDemo 控制台项目可以或许正确的引用上 UnoDemo 项目,必要修改项目文件,修改之后的代码大概如下
      Exe    net8.0-desktop    enable    enable    true<Page x:
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:UnoDemo"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel
      HorizontalAlignment="Center"
      VerticalAlignment="Center">
    <TextBlock AutomationProperties.AutomationId="HelloTextBlock"
          Text="Hello Uno Platform"
          HorizontalAlignment="Center" />
    <Button x:Name="RunAvaloniaButton" HorizontalAlignment="Center" Content="Run Avalonia" Click="RunAvaloniaButton_OnClick"/>
</StackPanel>
</Page>核心就是设置和设置框架版本以及加上 UnoSingleProject 属性
编辑 LibDemo 控制台项目,去掉 OutputType 定义,让其成为基础库项目,修改之后的 csproj 项目文件的代码如下
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
    <TargetFramework>net8.0</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
</PropertyGroup>

</Project>现在 UnoDemo 和 AvaloniaIDemo 项目相互之间不引用,为了可以或许间接调用到,就必要让两个项目都依赖抽象
在 LibDemo 项目里面提供简单的接口和静态范例用于被注入,代码如下
namespace LibDemo;

public interface IAppRunner
{
    void Run();
}以上的 IAppRunner 用于给具体的框架使用,无论是 Avalonia 先启动也会,照旧 Uno 先启动也好,只要后被启的框架实现此接口。再注入到下面定义的 Runner 静态方法里面,即可完成被调用的依赖
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace LibDemo;

public static class Runner
{
    public static void SetAppRunner(IAppRunner appRunner)
    {
      _appRunner = appRunner;
    }

    public static void Run() => _appRunner?.Run();

    private static IAppRunner? _appRunner;
}在本文例子里面依然是 Uno 先起来,然后调起 Avalonia 项目,根据上文的设计,必要在 AvaloniaIDemo 项目里面定义具体的实现逻辑,代码如下
using LibDemo;

namespace AvaloniaIDemo;

public static class Initializer
{
    public static void InitAssembly()
    {
      Runner.SetAppRunner(new AppRunner());
    }
}

file class AppRunner : IAppRunner
{
    public void Run()
    {
      Program.Main([]);
    }
}以上的 file class AppRunner 使用到了 file 关键字,这是表示当前的 AppRunner 范例只有在当前文件可访问。通过此关键字可以更大程度举行控制访问范围
如此即可在 AppDemo 里面,通过调用 InitAssembly 方法完成 AvaloniaIDemo 项目的初始化,代码如下
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AppDemo;

internal class Program
{
   
    public static void Main(string[] args)
    {
      AvaloniaIDemo.Initializer.InitAssembly();

      ... // 忽略其他代码
    }
}完成 AvaloniaIDemo 的注入之后,在 UnoDemo 即可使用抽象的依赖调用,如以下代码
    private void RunAvaloniaButton_OnClick(object sender, RoutedEventArgs e)    {      var thread = new Thread(() =>      {<Page x:
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:UnoDemo"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel
      HorizontalAlignment="Center"
      VerticalAlignment="Center">
    <TextBlock AutomationProperties.AutomationId="HelloTextBlock"
          Text="Hello Uno Platform"
          HorizontalAlignment="Center" />
    <Button x:Name="RunAvaloniaButton" HorizontalAlignment="Center" Content="Run Avalonia" Click="RunAvaloniaButton_OnClick"/>
</StackPanel>
</Page>Runner.Run();      })<Page x:
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:UnoDemo"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel
      HorizontalAlignment="Center"
      VerticalAlignment="Center">
    <TextBlock AutomationProperties.AutomationId="HelloTextBlock"
          Text="Hello Uno Platform"
          HorizontalAlignment="Center" />
    <Button x:Name="RunAvaloniaButton" HorizontalAlignment="Center" Content="Run Avalonia" Click="RunAvaloniaButton_OnClick"/>
</StackPanel>
</Page>   ... // 忽略其他代码    }可以看到 UnoDemo 从本来的具体的 AvaloniaIDemo.Program.Main([]); 换成了间接的 Runner.Run(); 调用
修改之后的 UnoDemo 的 MainPage.xaml.cs 的所有代码如下
using LibDemo;namespace UnoDemo;public sealed partial class MainPage : Page{    public MainPage()    {      this.InitializeComponent();    }      private void RunAvaloniaButton_OnClick(object sender, RoutedEventArgs e)    {      // Create the new Thread to run the Avalonia         var thread = new Thread(() =>      {<Page x:
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:UnoDemo"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel
      HorizontalAlignment="Center"
      VerticalAlignment="Center">
    <TextBlock AutomationProperties.AutomationId="HelloTextBlock"
          Text="Hello Uno Platform"
          HorizontalAlignment="Center" />
    <Button x:Name="RunAvaloniaButton" HorizontalAlignment="Center" Content="Run Avalonia" Click="RunAvaloniaButton_OnClick"/>
</StackPanel>
</Page>Runner.Run();      })      {<Page x:
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:UnoDemo"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel
      HorizontalAlignment="Center"
      VerticalAlignment="Center">
    <TextBlock AutomationProperties.AutomationId="HelloTextBlock"
          Text="Hello Uno Platform"
          HorizontalAlignment="Center" />
    <Button x:Name="RunAvaloniaButton" HorizontalAlignment="Center" Content="Run Avalonia" Click="RunAvaloniaButton_OnClick"/>
</StackPanel>
</Page>IsBackground = true,<Page x:
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:UnoDemo"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel
      HorizontalAlignment="Center"
      VerticalAlignment="Center">
    <TextBlock AutomationProperties.AutomationId="HelloTextBlock"
          Text="Hello Uno Platform"
          HorizontalAlignment="Center" />
    <Button x:Name="RunAvaloniaButton" HorizontalAlignment="Center" Content="Run Avalonia" Click="RunAvaloniaButton_OnClick"/>
</StackPanel>
</Page>Name = "Avalonia main thread"      };      if (OperatingSystem.IsWindows())      {<Page x:
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:UnoDemo"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel
      HorizontalAlignment="Center"
      VerticalAlignment="Center">
    <TextBlock AutomationProperties.AutomationId="HelloTextBlock"
          Text="Hello Uno Platform"
          HorizontalAlignment="Center" />
    <Button x:Name="RunAvaloniaButton" HorizontalAlignment="Center" Content="Run Avalonia" Click="RunAvaloniaButton_OnClick"/>
</StackPanel>
</Page>thread.SetApartmentState(ApartmentState.STA);      }      thread.Start();    }}完成基础逻辑之后,即可在 AppDemo 将 Uno 应用启动,代码如下
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace AppDemo;

internal class Program
{
   
    public static void Main(string[] args)
    {
      AvaloniaIDemo.Initializer.InitAssembly();

      UnoDemo.Program.Main(args);
    }
}接下来为了让整体构建更加简单,必要修改让 Avalonia 项目去掉 OutputType 属性,保持输出为基础库方式
<OutputType>WinExe</OutputType>修改之后的 AvaloniaIDemo 的 csproj 项目文件的代码如下
      net8.0    enable    true    app.manifest    true<Page x:
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:UnoDemo"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel
      HorizontalAlignment="Center"
      VerticalAlignment="Center">
    <TextBlock AutomationProperties.AutomationId="HelloTextBlock"
          Text="Hello Uno Platform"
          HorizontalAlignment="Center" />
    <Button x:Name="RunAvaloniaButton" HorizontalAlignment="Center" Content="Run Avalonia" Click="RunAvaloniaButton_OnClick"/>
</StackPanel>
</Page><Page x:
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:UnoDemo"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel
      HorizontalAlignment="Center"
      VerticalAlignment="Center">
    <TextBlock AutomationProperties.AutomationId="HelloTextBlock"
          Text="Hello Uno Platform"
          HorizontalAlignment="Center" />
    <Button x:Name="RunAvaloniaButton" HorizontalAlignment="Center" Content="Run Avalonia" Click="RunAvaloniaButton_OnClick"/>
</StackPanel>
</Page><Page x:
      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
      xmlns:local="using:UnoDemo"
      Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
<StackPanel
      HorizontalAlignment="Center"
      VerticalAlignment="Center">
    <TextBlock AutomationProperties.AutomationId="HelloTextBlock"
          Text="Hello Uno Platform"
          HorizontalAlignment="Center" />
    <Button x:Name="RunAvaloniaButton" HorizontalAlignment="Center" Content="Run Avalonia" Click="RunAvaloniaButton_OnClick"/>
</StackPanel>
</Page>可以看到 AvaloniaIDemo 项目没有对 UnoDemo 项目举行任何的引用
如此即可尝试直接 F5 运行。以及发布之后运行
以上方式我在 Windows 上 F5 直接运行乐成,发布到 Ubuntu 和 UOS 上也能运行乐成,看起来属于坑比较少的方式
本文以上代码放在 github 和 gitee 上,可以使用如下命令行拉取代码
先创建一个空文件夹,接着使用命令行 cd 命令进入此空文件夹,在命令行里面输入以下代码,即可获取到本文的代码
git init
git remote add origin https://gitee.com/lindexi/lindexi_gd.git
git pull origin 6cc41fa8c98001d034a59d20cad83386ae16b5aa以上使用的是 gitee 的源,如果 gitee 不能访问,请替换为 github 的源。请在命令行继续输入以下代码,将 gitee 源换成 github 源举行拉取代码
git remote remove origin
git remote add origin https://github.com/lindexi/lindexi_gd.git
git pull origin 6cc41fa8c98001d034a59d20cad83386ae16b5aa获取代码之后,进入 AvaloniaIDemo/WawjejokeniDejeyaibeweji 文件夹,即可获取到源代码

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: dotnet 融合 Avalonia 和 UNO 框架