65.9K
CodeProject 正在变化。 阅读更多。
Home

多平台 XAML/C# 神奇套装:Avalonia。将 Avalonia 与基于 WinUI 的解决方案进行比较

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.96/5 (43投票s)

2023 年 8 月 20 日

MIT

38分钟阅读

viewsIcon

48960

本文介绍了 Avalonia 作为一种新的多平台 XAML/C# 解决方案,可在其已存在一段时间的桌面功能之上,用于 Web 和移动开发平台。

免责声明

在本文中,我仅发表个人观点。

Avalonia 11 - 新的强大跨平台套件

近年来,软件应用公司面临客户压力,要求不仅提供桌面解决方案,还要提供包含大部分甚至全部桌面功能的 Web 和移动解决方案。此外,为了增加客户群,为主要平台(Windows、Mac 和 Linux)创建桌面应用程序总是好事。

在今年夏天发布 Avalonia 11 之前——提供(成熟度不同,取决于平台)桌面(Windows、Mac 和 Linux)、Web(WASM)和移动(Android、iOS 和 Tizen)解决方案的唯一跨平台参与者是 Uno Platform

Microsoft 的 MAUI - 不提供创建非常重要的 Web 和 Linux 桌面应用程序的功能。此外,我听说 MAUI 桌面功能不够稳定,无法用于生产应用程序。

在当前的竞争平台(主要是 Avalonia、Uno,以及较小程度上的 MAUI)中 - 正如我下面在本文中所示,Avalonia 是跨多个平台最强大、最统一的。

本文适合谁?

适用于决策者、产品和项目经理、架构师和开发人员。

本文包含大量 XAML/C# 代码示例,但对于不熟悉代码的人来说,可以跳过所有这些示例。

为何要跨平台?

许多公司现在面临压力,需要为包括 Windows、Linux 和 MacOS 在内的多个平台提供 UI 解决方案。在我最近的几个合同中,涉及 Windows 和 Linux,其中一个则涉及 Windows 和 MacOS。

此外,将解决方案移植到 Web 的需求日益增长,提供产品的大部分(但不一定是全部)功能。

当然,为您的产品提供移动 UI 总是好的,特别是对于在旅行中(例如飞机上)可以执行的工作流程。

为什么我们需要一个单一框架来构建跨平台解决方案

为什么我们不能用 WPF 为 Windows 构建桌面解决方案,用 GTK 为 Linux 构建,用 HTML/JavaScript 为 Web 构建?

答案很明显——使用完全不同的框架技术创建、维护和扩展任何应用程序都需要付出更大的努力,而使用单一框架,公司可以一次性为其所有产品重用大量代码和开发人员的专业知识。

为什么是 XAML/C#?

许多公司拥有大量 WPF 或 Silverlight 代码,将其移植到 XAML/C# 框架比移植到 HTML/JavaScript 要容易得多。

XAML/C# 提供的编程范例比 HTML/JavaScript 强大得多。XAML/C# 代码更容易开发、调试和维护。

XAML 也可以与 C++ 一起使用(例如 QT 框架),但 C++ 的编译时间非常慢,这会减慢开发、调试和维护周期。大多数架构师和开发人员都同意,使用 C# 或 Java 构建应用程序比使用 C++ 要快得多。由于 Java 没有提供良好且灵活的 UI 解决方案 - 用 Java 构建的 UI 看起来与旧的金属 Unix UI 差不多,所以 C# 是主要选择。

本文讨论的跨平台 XAML 框架

我在这里比较的所有框架都是基于 XAML 的跨平台解决方案。

  • Avalonia - 最新版本支持桌面(Windows、Linux 和 MacOS)、Web(通过 WASM)和移动(Android、iOS 和 Tizen)
  • Uno Platform WinUI 实现支持桌面(Windows、Linux 和 MacOS)、Web(通过 WASM)和移动(Android、iOS 和 Tizen)
  • MAUI WinUI 实现支持桌面(Windows 和 MacOS)和移动(Android、iOS 和 Tizen)- 不支持 Web 或 Linux

从上表可以看出,MAUI 相对于 Avalonia 和 Uno 的第一个缺点是 - 它不允许产品在 Web 上运行,并且不提供 Linux 的桌面解决方案。

我对微软的态度

在本文中,我将对微软最近的 UI 套件说一些严厉的话,所以我想澄清一下,我与微软没有像 Richard Stallman 那样的主要意识形态分歧。我只是作为一个使用它们构建产品的人来评判各种套件。

除了他们当前的 UI 部门,我实际上很欣赏微软,因为他们开发并分享了 C# - 我认为这是最好的、最强大、发展最快的软件开发语言。此外,他们使 C# 的大部分(不包括 UI)都能在包括 Linux 和 Mac 在内的多个平台运行,并以限制最少的 MIT 许可证开放了其源代码。

微软还在 LINQ、Tasks (Promises) 和 Reactive Extensions 等各种编程范式方面处于领先地位,并比其他语言更早地将它们集成到 C# 中。

微软为当时最好的 Windows 开发框架 - WPF 创建,并提出了多种 UI 开发概念,包括 MVVM 模式 - UI 开发的最佳模式。

微软创建了最佳的 Web 开发框架 - ASP.NET,并不断努力改进它。

微软创建了 JavaScript 的强类型版本 - TypeScript,如果您选择 HTML/JavaScript 作为开发平台,我强烈建议您使用 TypeScript 而不是 JavaScript - HTML/JavaScript 开发团队越大,对 TypeScript 的需求就越大。

微软创建了 WSL - Windows 子系统 for Linux,使得在 Windows 上开发和调试 Linux UI 和控制台应用程序变得轻而易举。

除此之外,微软目前在许多领域都处于领先地位,包括其云服务(Azure)、人工智能、嵌入式系统等。

我的背景和与 Avalonia、Uno 和 MAUI 的关系

我是顶级的 WPF/MVVM 架构师和开发人员之一。

几年前,我发现了 Avalonia,并一见钟情。

在本文中,我将说很多关于 Avalonia 的好话,对其他框架说得少一些,所以我想强调一下**除了**

  • 一见钟情于 Avalonia
  • 构建多个开源 Avalonia 库
  • 在 CodeProject 上撰写多篇关于 Avalonia 的文章
  • 几年前帮助他们进行文档编写,并为此获得了总计 2500 美元(也是几年前)
  • 在过去两年中,为我的几个合同使用 Avalonia 桌面解决方案。
  • 现在,在 2023 年底 - 在文章最初发布很久之后,我已成为 Avalonia MVP

Avalonia 没有赞助我,他们也不知道我写这篇文章。

Uno Platform

在我的一份合同中,我广泛使用了 Uno Platform 大约 6 个月,用它构建了一些复杂的控件,包括一个 Gantt Chart 控件,以及一个模仿并提供 Excel 电子表格大部分功能的控件。我还开发和维护了一个复杂的、成熟的 Uno Platform 应用程序,这是从一个旧的 WPF 应用程序移植过来的。

在我的 Uno 工作中,我主要专注于桌面和 Web 平台。

MAUI

我从未在任何合同中使用过 MAUI,只是玩过它,包括在写这篇文章的时候。

本文内容是否可信?

我认为 - 是的,因为我陈述的几乎所有内容都有具体示例支持,并且尽管我宣称热爱 Avalonia,但我会尽量保持客观。

有时,我会使用猜测,但在这种情况下 - 我会特别说明这只是我的猜测。

文章的主要结论

Avalonia、Uno 和 MAUI 都可以使用,我自己也看到过用 Avalonia 和 Uno Platform 创建的产品。至于 MAUI - 作为 Xamarin 的下一版本,我也知道很多用 Xamarin 创建的移动产品。

Avalonia 是比 Uno 或 MAUI 更好的多平台开发套件。

现在我说的“更好”是什么意思 - 它允许产品的开发、维护和支持速度大大加快。特别是

  • 它是一个更强大的 UI 套件,允许更多的代码重用,并能更快地编写更短的代码来实现相同的功能。部分原因是它不受 WinUI/UWP 的限制 - 它实际上比 WPF 更强大,而 WPF 又比 WinUI 或 UWP 更好。另一个原因是它具有更好的实现,并且在所有平台之间有很多可重用的代码,而 Uno 和 MAUI 本质上为它们覆盖的每个平台都有完全不同的实现。
  • Avalonia 中有许多 Uno 或 MAUI 中未实现的功能,而我不知道 Uno 或 MAUI 中实现的任何功能无法在 Avalonia 中工作。如果有人知道 Uno 或 MAUI 中提供的任何 Avalonia 中没有的功能,请在评论中提及,我将在本文中引用。
  • Avalonia 覆盖了与 Uno 相同的所有平台,并且比 MAUI 覆盖的平台更多,在不同平台之间的行为差异也相当少。
  • 对于 WPF 专家来说,切换到 Avalonia 比切换到 Uno 或 MAUI 要容易得多。

文章的其余部分只是为了证明上述观点。

比较 XAML/C# 框架的架构

Avalonia 比 UNO Platform 和 MAUI 有两个优势

  1. Avalonia 不局限于 WinUI/UWP 的功能 - 事实上,Avalonia 在许多方面比 WPF 强大得多 - 它是多平台 WPF++,而 WinUI(和 UWP)API 是 WPF API 的苍白模仿(WPF--)。我将在下面更详细地介绍,详细介绍 WinUI 缺少的一些 API 并提供具体示例。
  2. Avalonia 的设计理念是尽可能多地为不同平台重用代码。他们将框架构建在 Skia 之上 - 这是一个很棒的底层跨平台框架。对于无法使用 Skia 的部分,他们通过特定平台解决方案来实现。UNO 和 MAUI 的理念恰恰相反 - 他们尽可能多地使用每种平台的原生代码,并且他们为所覆盖的每个平台都有完全不同的实现,因为他们试图尽可能多地利用原生平台的功能。

这是 Avalonia 的架构图。

Polyfill 是通用的 API(接口),具有平台依赖的实现。正如您所见,特定平台代码仅占 Avalonia 总代码的一小部分,而大部分代码在每个平台上都是相同的。

这是 Uno Platform 的架构图。

每个实现都有完全不同的代码 - 事实上,Web 实现生成 HTML,而 Windows 实现使用 WinUI 的原生调用;Linux - Skia,而移动和 MacOS 实现使用 Xamarin。因此,Uno 不得不维护 4 个不同的代码版本,并且可用功能在不同平台上的表现也不同 - 我对此非常了解,并且将在下面进一步讨论。

这是 MAUI 的架构图。

MAUI 覆盖的平台更少,但存在与 Uno 相同的所有缺点

  1. 它试图实现 WinUI(这是 WPF--)
  2. 它尽可能多地使用原生代码 - 因此每个平台基本上都有自己的实现。

WASM 游乐场

Avalonia 和 Uno 都有 Web (WASM) 游乐场,您可以查看 - Avalonia PlaygroundUno Playground

MAUI 没有 Web 游乐场(因为没有 MAUI Web 解决方案)。

微软 XAML/C# 开发简史

WPF

大约 20 年前,微软一群非常有才华、在 UI 开发方面经验丰富的人员为 Windows 桌面开发推出了一款很棒的新 .NET 套件。该项目最初名为 Avalon,后来被命名为 WPF(Windows Presentation Foundation)。

WPF 为什么很棒?因为

  • 它的底层功能允许在 Windows 上创建和定制几乎任何 UI,以完全符合客户的要求。
  • 它带来了许多高级功能,代表了新发现和形式化的 UI 概念,使得可以实现很好的关注点分离和重用。特别是
    1. WPF 将**组合**的概念提升到了一个新的水平 - 几乎任何简单的控件都可以由边框、面板、形状、图像和文本等基本图元组合而成。
    2. **视觉树和逻辑树**反映了 WPF UI 控件的组合层次结构。
    3. **依赖属性和附加属性**用于节省内存,并允许在不修改元素类的情况下将值附加到 WPF UI 元素的新属性上。
    4. **绑定**允许将目标属性绑定到源属性。WPF UI 元素上的几乎所有属性都可以绑定。绑定与 DataTemplates 一起最终形成了 MVVM 模式,实现了视觉代码和非视觉代码的完全分离,使得非视觉代码包含大部分复杂性,而视觉代码则被动地模仿非视觉代码。
    5. **路由事件**(也可以是**附加路由事件**)在视觉树上下传播。例如,一个包含的元素可以触发这样的事件,但它将在包含的元素上处理。
    6. **控件模板** - 允许重用 XAML 的部分。
    7. **数据模板** - 允许将一些非视觉数据转换为视觉数据。
    8. **样式** - 允许轻松地重新设置 WPF 控件的样式。样式也可以从其他样式继承。
    9. **模板和样式触发器**允许轻松修改行为。
    10. 它允许使用一些底层图形机制来构建更快的控件。
    它还包含许多其他不太重要的概念和改进,我在此省略。
  • WPF 与 MS Visual Studio(XAML intellisense 和 Designer)集成良好,并且拥有最好的开源 Spy 工具 Snoop。

许多才华横溢的人发现了 WPF 的潜力,立即加入了 WPF 的潮流,并创建了一个名为**WPF Disciples**的互联网社区。我可以举出 Karl Schifflett、Josh Smith、Peter O'Hanlon、Sacha Barber、Beatrix Costa、Rob Eisenberg 的名字。还有许多其他人,我此刻记不起他们的名字了。

在微软放弃 Silverlight 并将 WPF 置于次要地位,转而提供可怜的替代品(UWP 和 WinUI)之后,这些人中的许多人转向了其他(非微软)技术。

现在,想想互联网社区的名称 - **WPF Disciples**。创建这个互联网社区的人们明白,WPF 不仅仅是另一个可视化开发套件。他们明白 WPF 是一种新的开发哲学 - 本质上是一套新的、非常深入且有用的范例,可以实现更快的开发并创建更精简、易于维护和扩展的代码。此外,许多 WPF 功能也可以用于非视觉开发(尽管我认为它们目前并未广泛用于视觉开发之外)。

有些人抱怨 WPF 太复杂难以学习。我的回答是,WPF 不比可视化编程更难。WPF 没有许多不必要的功能 - 这些功能对于好的可视化程序员来说不是必需的。

WPF 肯定有一个学习曲线,但一旦您了解并理解了 WPF,您就可以非常快速、精简地编写复杂的产品。

基本上 WPF 有一个进入门槛,但一旦您了解并理解了它 - 您就会爱上它,它会给您带来巨大的开发能力。

Silverlight(已故)

WPF 有一个主要的缺点 - 它不是跨平台的。因此,在某个时候,Scott Guthrie 领导下的微软部门创建了 Silverlight(原名 WPF everywhere)。

Silverlight 拥有许多(但不是全部)WPF 功能 - 我不断遇到一些怪癖,例如,自定义附加属性不能被动画化的奇怪限制以及许多其他问题。但它有一个巨大的优点:它(及其 .NET 框架版本 - 也不够完整)可以在多个平台上运行。

起初,Silverlight 作为大多数流行浏览器的浏览器插件发布,但后来微软也提供了桌面版本。

微软 UI 编程乐园的麻烦 - 应用部门的攻击

说到上面的图片 - 我不知道微软的哪个部门是坏人,哪个是好人 - 我认为每个部门都有点像。

显然,微软的 Windows 和应用部门之间存在一些紧张关系。Windows 部门主要使用 C++,而应用部门主要使用 C#。

受到 C# 和 WPF/Silverlight 成功的鼓舞的 .NET C# 专家们希望用 .NET Framework 重写整个微软操作系统。无论是因为 Windows 部门的合作不佳还是由于其他原因,但这项努力基本上导致了 MS Vista 操作系统,该操作系统在公司和开发者中都不受欢迎。

微软内部出现更多麻烦(Windows 部门反击)

大约在 2010-2011 年,微软意识到不构建自己的移动平台是一个错误。到那时,苹果和谷歌在市值上远远领先于微软,因为它们在智能手机/平板电脑市场占据主导地位。

苹果是第一个创建移动平台的(同时,他们锁定了自己的操作系统)。谷歌是第二个进入市场的,但由于谷歌的产品更便宜,而且谷歌的 Android 系统是开源的,最终谷歌占据了移动市场份额的一半以上。

尽管微软正确地识别了问题,但(毫无疑问是在其赚钱的 Windows 部门的影响下)得出了以下所有错误的结论:

  • C++ 应该是编写大多数应用程序的语言。(C++ 编译速度很慢,所以与 C# 或 Java 相比,它不是一个好的应用程序开发语言)。
  • HTML5/JavaScript 足以进行多平台应用程序开发。(TypeScript 或任何新的 JavaScript 框架 - Angular、Vue 或 React - 都远不及 WPF 在功能、开发速度、易维护性方面)。
  • 因此,Silverlight/WPF 和 C# 应该被放弃或接近放弃。
  • 开始按钮(人们用它在台式机和笔记本电脑上启动 Windows 应用程序)应该被放弃(这相当荒谬,因为桌面/笔记本电脑和移动的使用场景完全不同,每个都应该区别对待并使用不同的 UI)。
  • 微软应该努力像苹果一样封闭自己的系统。(在移动市场排名第三,他们试图表现得像市场领导者)。

这种态度实际上导致了在创建自己的移动平台和硬件 - Windows RT(无法运行 .NET Framework 和 C#)上浪费了数十亿美元。Windows RT(作为独立操作系统)和微软移动硬件最终被埋没,原因是没有人感兴趣,这反过来又是由以下原因造成的:

  • 缺乏应用程序开发 - 开发人员不喜欢新平台。
  • 操作系统不是开源的(例如,像 Android 一样),人们担心它会被完全封闭,以至于只有微软才能决定允许哪个应用程序,哪个不允许 - 就像苹果对 Silverlight 移动版所做的那样。
  • 对微软下一步行动缺乏信心。
  • Windows 8 操作系统失败 - 这很大程度上是由于缺乏**开始**按钮,并且对客户的抱怨置若罔闻。

妥协

最终,MS 应用开发者说服微软至少暂时保留 C# 和 WPF 的开发,后来,Windows 部门所倡导方法的失败证实了 C# 和 .NET 的有效性,因此它们不仅没有被放弃,而且变成了跨平台的(可在 Windows、Linux、Mac、嵌入式系统以及通过 WASM 在浏览器中运行),并成为发展最快、使用最广泛的平台/语言之一。

甚至在 C++ 被证明不适合应用程序开发之前,粉丝们就成功地捍卫了 XAML,因此后续的 Windows 套件 UWP 及其更近期的替代品 WinUI 都是基于 XAML 的。

什么是 UWP 和 WinUI

UWP 和 WinUI 都可以称为 WPF--(WPF 减减 - WPF 的苍白模仿)。它们都提供了 Windows 功能的包装器,使得 API 有点类似于 WPF/Silverlight。然而,许多非常有用的功能缺失 - 我将在下面提供更多关于缺失功能的细节。

对于 WPF 开发人员来说,切换到 UWP 或 WinUI 相当于从哈雷戴维森摩托车退回到自行车。这就是为什么许多开发人员要么坚持使用 WPF,要么转向非微软技术。

**以某案为例**:在微软推广 UWP 十多年以及推广 WinUI 几年后 - 在 Dice 上找不到任何 UWP 或 WinUI 的工作,而有 120 个 WPF 的工作(这是我写这篇文章时的情况 - 当然,数字每天都会变化,但通常保持接近)。

显然,微软的 UI 开发已被边缘化 - 没有兴趣,UI 团队可以发布一个又一个近乎失败的产品而不承担任何后果。

Avalonia 登场

一些热爱并理解 WPF 的开发人员聚在一起,创建了一个名为 Avalonia 的新的开源、跨平台框架。如果 UWP 和 WinUI 是 WPF--,那么 Avalonia(即使不考虑其跨平台性)就是 WPF++。它几乎拥有 WPF 的所有功能,并且还有一些额外功能,使得编程 Avalonia 更加强大和高效。

与竞争对手不同,Avalonia 试图最大限度地减少对原生代码的依赖,而是使用 Skia(用于底层视觉元素的跨平台框架)。

直到最近,只有 Avalonia 的桌面解决方案是稳定的,但现在,Avalonia 11 已发布数周,它还通过 WASM 和移动平台(iOS、Android 和 Tizen)支持 Web。

Avalonia 完全开源,并附带最宽松的 MIT 许可证,这意味着您可以将其用于商业和非商业产品,并且可以自由修改代码,只要您注明原始代码来自 Avalonia。

Avalonia 的桌面版已通过 Avalonia 网站上列出的许多产品进行了彻底测试。

Uno Platform

Uno Platform 由 Uno 公司开发,其宗旨也是提供跨平台的桌面、Web 和移动解决方案。它与微软密切相关 - 其创始人中有几位是微软 MVP。因此,它也存在微软产品所面临的同样缺点。

本质上,Uno Platform 为 Windows、Linux、Mac、Web 和移动平台提供了 WinUI(或早期版本 - UWP)的实现。

同样遵循 Xamarin 和 MAUI 的例子,Uno 被实现为原生平台调用的包装器。这导致了完全不同的代码,例如,对于 Windows、Linux、移动和 Web。事实上,Uno for Windows 是作为 Windows 原生调用的包装器实现的,而其 Web 实现则依赖于生成 HTML/JavaScript 代码。

因此,在 Windows 平台上运行的功能在 Web 上可能无法正常运行。事实上,在使 Windows 版本正常工作后,您还需要切换到 Web 并使相同的功能在 Web 上也正常工作。并且可能需要一些时间来弄清楚如何做到这一点。bug 也一样:在 Windows 上可以工作的东西在 Web 上可能会崩溃,反之亦然。

我将在下面进一步讨论具体案例。

平均而言,在编程 Uno 时,我每周花费约 6-8 小时使 Web 版本表现得与 Windows 版本相似。

对于 Avalonia - 此类时间接近 0。

MAUI

MAUI 是 Xamarin 的新版本,也能创建桌面应用程序。到目前为止,我从未见过自己用 MAUI 构建的桌面应用程序,在一些论坛上,关于使用它进行桌面应用程序的观点普遍为负面。

它存在与 Uno 相同的问题,但覆盖的平台更少。

示例的通用描述

如上所述,Avalonia 是 WPF++,而 WinUI(正在取代 UWP)是 WPF--。此外,由于 Avalonia 的架构,一个平台上的功能在另一个平台上工作的几率要大得多。

此外,Avalonia 比 MAUI 或 Uno Platform 拥有更好的开发工具和更大的项目更快的编译速度。

相应地,比较分为以下几组

  1. 开发工具和调试能力的比较 - 仅文字,无代码示例。
  2. 代码示例,证明 Avalonia 拥有 WinUI(以及相应地 Uno 和 MAUI)缺失的所有重要功能。

请注意,为了理解示例代码,我期望人们对 XAML/C# 框架有所了解 - 最好是 WPF。

不了解 WPF 或其他 XAML/C# 套件的人可以跳过代码,只阅读文本。

平台覆盖、开发工具、第三方程序包和调试能力比较

开源

所有三个框架 - Avalonia、Uno Platform 和 MAUI 都是开源的,并以最宽松的 MIT 许可证发布。这意味着您基本上可以随意处理代码和程序包,在任何商业或非商业产品中使用它们,只要您注明原始代码来自相应的程序包。

所有三个程序包的源代码都位于 Github 上。以下是链接

  1. Avalonia
  2. UNO Platform
  3. MAUI

覆盖的平台

如上所述,Avalonia 和 Uno Platform 覆盖 Windows/Linux/MacOS 桌面,Web 以及 Android/iOS/Tizen 移动。

MAUI - 不提供 Linux 桌面或 Web 的解决方案 - 在某种意义上,它处于劣势。

第三方控件

复杂的第三方控件

另一个重要问题是第三方控件的可用性,特别是用于实现一些复杂想法,例如,桌面停靠/分组/标签控件、MS Excel、报表、PDF 或布局管理控件。除了我自己的 UniDock Docking/Grouping/Tabbing 解决方案(用于 Avalonia,我仍然需要升级才能与 Avalonia 11 一起使用)之外,Avalonia、Uno 或 MAUI 都没有为其内置这些复杂的控件。

知名组件公司开发的简单控件

在简单的控件方面,MAUI 显然具有优势,因为它背后有微软的品牌 - DevEx、Telerik 和其他主要公司都在为 MAUI 开发控件,尽管目前,这些控件不如 WPF 的复杂,并且主要服务于移动设备,而不是新的 MAUI 桌面解决方案。大多数大型公司目前提供的这些简单控件,在 Avalonia 和 Uno 中都可以轻松创建和设置样式。

DataGrid 和 Tabs 控件

Avalonia 和 UNO Platform 都拥有 DataGridTabControl,它们要么作为其源代码的一部分(Avalonia),要么作为开源程序包的一部分(UNO Platform)。

MAUI 内置了 TabBar 和一些商业解决方案用于 DataGrid,例如 Telerik 或 SyncFusion。

Charts

基于 Open GL 构建的几个图表具有兼容性(至少对于桌面和 Web Avalonia 程序包)。特别是,OxyPlotLiveCharts 和 ScottPlot 可以与 Avalonia 一起使用。

还有一个商业解决方案 - Tee Chart

Telerik 和 Syncfusion 为 MAUI 提供了商业图表组件。

Syncfusion 也为 Uno 提供了 Chart 控件 - 处于测试阶段。

即将推出

Actipro Controls for Avalonia.

产品速度

这是我的主观观点,Avalonia 产品通常在多个平台上的速度比 Uno 快。

一个例外是 - 浏览器中 Avalonia DataGrid 控件的滚动速度与相应的 Uno 控件一样慢(两者都在 AOT 编译下)。

Uno 和 MAUI 都是作为 Windows 上的 WinRT 原生调用的包装器构建的。WinRT 互操作存在一些众所周知的问题,这使得 WinUI 依赖项属性的性能非常慢。这个问题在以下链接中有详细说明: WinUI 3 性能提升

此外,github 问题链接:"Dependency property is much slower in WinUI 3 than in WPF" 表明微软自 2019 年 11 月以来就知晓此问题,但并未采取任何措施。

Spy 工具

WPF Snoop

WPF 有一个很棒的 UI Spy 工具 - Snoop。它允许

  1. 显示视觉树或逻辑树
  2. 快速在视觉树或逻辑树中查找元素
  3. 检查和修改元素上的附加属性和依赖属性
  4. 迭代深入(钻取)属性 - 允许检查复合属性的值
  5. 检查元素的 DataContext 并迭代深入其属性
  6. 查看事件传播 - 沿视觉树向下 - 用于隧道事件,沿视觉树向上 - 用于冒泡事件。这非常重要,例如,当您想弄清楚为什么某个事件没有到达视觉树上的某个元素时。
  7. 调用任何元素上的 public 方法或 public 属性。

Avalonia、UNO 或 MAUI 都没有像 WPF 那样强大的 Spy 工具。但 Avalonia 在这三者中拥有最好的 Spy 工具。

Avalonia Spy Tool

Avalonia Spy Tool 的功能远不如 Snoop(目前)。

它允许

  1. 显示视觉树或逻辑树
  2. 快速在这些树中查找元素
  3. 检查和修改元素上的附加 Avalonia 属性和依赖属性
  4. 监控视觉树上下路由事件的传播
  5. 深入(钻取)复合属性,但不显示其绑定的信息

它不允许

  1. 显示绑定的详细信息
  2. 调用元素和属性上的方法

Uno 和 MAUI

Uno 和 MAUI 只允许使用 Microsoft Visual Live Tree 和 Live Properties,这比 Avalonia Spy Tool 差,因为

  1. 它只在您在 Visual Studio 中调试应用程序时才有效。
  2. 它只显示视觉树(WinUI 中没有逻辑树的概念)。
  3. 它不允许查看路由事件的传播。
  4. 它会跳过一些元素,特别是在 DataGrid 等复杂控件上。
  5. 对于某些控件,它不显示其属性 - 我还没有找到任何规则,但通常发生在某些复杂控件的部分,例如 DataGrid
  6. 通常,它会无故不允许修改属性,因为该属性是 public 且可设置的。

它提供了两项功能,而 Avalonia Spy Tool 没有这些功能:

  1. 可以立即跳转到 Live Visual Tree 中找到的元素的 *code*。
  2. 会显示有关绑定的信息。

尽管有这些额外功能,我发现使用 Avalonia Spy Tool 比 Live Visual Tree/Live Properties 组合更好,而 WPF Snoop 则以相当大的优势胜过所有这些工具。

用于运行比较示例的 Visual Studio 版本

我使用的是 Visual Studio 2022 17.7 Preview 2.0。

这并不意味着您必须使用相同的版本 - 对于大多数示例,VS 2022 版本无关紧要。

用于示例的 Avalonia Framework

Avalonia 11.04

有关示例的信息

总的来说,示例分为两组 - Visual Samples 和 API Samples。两者都没有提供 Avalonia 在所有平台上的功能的详尽列表,而这些功能在竞争框架中不可用,但我试图列出最重要和令人印象深刻的。

示例介绍

为了扩大本文的受众,我将展示一些示例,而不深入讨论代码,尽管本文的所有代码都可用,并可以在 Avalonia Comparison Samples 存储库中找到。

以下所有示例均在 Windows 11、Linux(通过 WSL)、Chrome Web 浏览器和 Android 模拟器上进行过测试。

要在 Linux 上运行桌面示例 - 请阅读我关于它的文章 - 在 Windows Subsystem for Linux (WSL) 上运行和调试多平台 .NET GUI 和控制台应用程序

目前,我无法在 MacOS、iOS 和 Tizen 平台上运行示例。

以下所有示例在 Avalonia 中都能正常运行,但在 Uno Platform 或 MAUI 中则无法运行,无论是在所有平台还是在相应示例中提到的特定平台。

在其他框架跨平台不可用的 Avalonia 可视化多平台功能示例

模糊页面示例

有时,您想在不让客户使用的情况下预览一些功能。一种常用的做法是使用模糊效果,让客户可以看到一些文本和按钮,但无法辨认其中的内容。WinUI 具有模糊效果,因此可以在 Uno Platform for Windows 上使用它,但在 Uno Web 版本中缺少这种效果,因为 Uno Web 是通过生成 HTML/JavaScript 来实现的,而浏览器没有能力模糊其 HTML 树的某些部分。

要模糊化大部分功能,可以使用 Acrylic Brush - 但它不适用于模糊化小功能,例如文本,同时又能让人们看到写了什么。

Avalonia 模糊示例的代码位于 NP.Demos.BlurSample 项目下。

以下是每个平台上模糊页面的图像

Windows 桌面

Linux 桌面(通过 WSL)

浏览器中的模糊效果

Android

创建和运行多平台 Avalonia 项目

本节仅适用于想要自己运行示例和/或使用 Avalonia 进行开发的人。

要创建 Avalonia 11 的多平台项目,您需要为您的 Visual Studio 2022 安装 Avalonia Template Studio 扩展。

创建新项目时,从 Template Studio 中选择“Avalonia C# Project”。

在下一个屏幕上,选择项目位置和名称,然后按 **Create** 按钮。您将看到以下内容:

选择您的应用程序要运行的平台,然后按底部的 **Create** 按钮。

它将创建一个可重用的项目和几个项目来运行应用程序的每个平台。

BlurSample Solution 中,NP.Demos.BlurSample 项目是可重用的,NP.Demos.BlurSample.Desktop 用于桌面应用程序(Windows、Linux 和 MacOS),NP.Demos.BlurSample.Browser - 在浏览器中运行(通过 WASM),NP.Demos.BlurSample.Android - 在 Android 或 Android 模拟器上运行。

MainView.axaml 文件及其 C# 对应文件 - MainView.axaml.cs 用于创建主应用程序。它将由桌面、浏览器和移动应用程序使用。

请注意MainWindow.axaml/MainWindows.axaml.cs 文件仅供桌面应用程序使用,不会在移动或浏览器应用程序中使用。因此,您希望 Web 和移动设备能够访问的功能不应放置在那里。

BlurSample 代码

BlurSample 代码非常简单 - 唯一的更改在 MainView.axaml 文件中。

<Grid RowDefinitions="*,*">
    <Grid.Effect>
        <BlurEffect Radius="5"/>
    </Grid.Effect>
    <TextBlock Text="Welcome to Avalonia"
                HorizontalAlignment="Center"
                VerticalAlignment="Center"/>

    <Rectangle Width="30"
               Height="30"
               Fill="Red"
               Grid.Row="1"
               HorizontalAlignment="Center"
               VerticalAlignment="Center"/>
</Grid>  

元素到图像示例

构建软件应用程序时,您可能经常希望截取屏幕的某一部分,以便保存或用于打印。

Avalonia 在每个平台上(通过三种不同的方式)为您提供了此功能。

Uno 提供了此功能,但有一个明显例外 - 您无法在 Web 浏览器中将屏幕的某些部分转换为图像:[WASM] Support for RenderTargetBitmap

据我所知,MAUI 允许在它所覆盖的每个平台上进行屏幕截图。

打开并启动您想要的平台上的 NP.Demos.ElementToImage 项目。按底部的 **Render** 按钮,包含 TextBlockRectangleCombobox 的顶部元素将在下面的三个不同行中渲染 - 最后两行带有平铺(尽管平铺可以关闭)。

这是生成后的图像

Windows

Linux

浏览器

Android

为简洁起见,我在此不详细介绍代码,但唯一的变化是在 MainView.axamlMainView.axaml.cs 文件中。

前两行图像是通过 RenderTargetBitmap 转换为 Bitmap 来实现的。第一行图像显示一个 Image 控件,其 Source 属性设置为 Bitmap

第二行图像是通过一个 Panel 实现的,其背景由 ImageBrush 创建。

第三行图像代表了从控件创建图像的最简单方法 - 通过 VisualBrush。请注意,与 WPF 不同,VisualBrush 不会自动反映控件的变化。

三维示例

下一个示例的代码是我从 Avalonia 源代码 Render Samples 复制的,并使其在不同平台上运行。请在 NP.Demos.Complex3DSample 解决方案中找到它。

Avalonia 借助 Rotate3DTransform 允许在多个平台上运行一些 3D 功能,据我所知,Uno 和 MAUI 都不允许。以下是运行示例的结果。

Windows

Linux

浏览器

Android

裁剪和不透明度蒙版示例

据我所知,除了 Windows 桌面平台之外,Uno Platform 上没有实现裁剪或不透明度蒙版。对于 MAUI,实现了裁剪,但没有实现不透明度蒙版。

Clipping/OpacityMask 示例位于 NP.Demos.ClippingSample 解决方案中。

Windows

Linux

Web 浏览器

Android

LayoutTransformControl 示例

对于 Uno,可以使用 CommunityToolkit 的 LayoutTransformControl 在 Windows 上,但在 Web 上不行(我不确定移动设备)。

Avalonia LayoutTransformControl 示例位于 NP.Demos.LayoutTransformControlSample 解决方案中。

首先,它为什么是必需的。

该示例由视图模型 MainViewModel 组成,它是一个包含 1000 个句子的 ObservableCollection<string>,并包含一个双向可通知的 ScaleFactor 属性。

public class MainViewModel : ObservableCollection<string>, INotifyPropertyChanged
{
    public MainViewModel()
    {
        for (int i = 0; i < 1000; i++) 
        {
            Add($"This is sentence {i}.");
        }
    }

    #region ScaleFactor Property
    private double _scaleFactor = 1d;
    public double ScaleFactor
    {
        get
        {
            return this._scaleFactor;
        }
        set
        {
            if (this._scaleFactor == value)
            {
                return;
            }

            this._scaleFactor = value;
            this.OnPropertyChanged(new PropertyChangedEventArgs(nameof(ScaleFactor)));
        }
    }
    #endregion ScaleFactor Property
}  

MainView 代表一个 DataGrid,每行有一个列 - 对应视图模型中的一个句子,以及底部的滑块。

<Grid RowDefinitions="*,40">
    <LayoutTransformControl HorizontalAlignment="Stretch"
                            VerticalAlignment="Stretch"
                            RenderTransformOrigin="0,0">
        <DataGrid ItemsSource="{Binding}"
                    RenderTransformOrigin="0,0">
            <DataGrid.Columns>
                <DataGridTextColumn Binding="{Binding}"
                                    Header="Text"/>
            </DataGrid.Columns>
            <!--<DataGrid.RenderTransform>
                <ScaleTransform ScaleX="{Binding ScaleFactor}"
                                ScaleY="{Binding ScaleFactor}"/>
            </DataGrid.RenderTransform>-->
        </DataGrid>
        <LayoutTransformControl.LayoutTransform>
            <ScaleTransform ScaleX="{Binding ScaleFactor}"
                            ScaleY="{Binding ScaleFactor}"/>
        </LayoutTransformControl.LayoutTransform>
    </LayoutTransformControl>
        
    <Slider Grid.Row="1" 
            Margin="10,0"
            Value="{Binding Path=ScaleFactor, Mode=TwoWay}"
            HorizontalAlignment="Center"
            VerticalAlignment="Center"
            Orientation="Horizontal" 
            Minimum="1"
            Maximum="5"
            Width="300"/>
</Grid>  

滑块的 Value 与视图模型上的 ScaleFactor 进行TwoWay绑定,并且值从 1 更改到 5

首先 - 让我演示一下为什么需要 LayoutTransformControl,为什么不能使用 RenderTransform

注释掉 LayoutTransformControlScaleTransform,并取消注释 DataGrid.RenderTransform 中类似的 ScaleTransform

现在,必须将 DataGrid 适配到分配空间的布局规则将不被遵守,一旦您通过移动滑块稍微增加 ScaleFactor - 右侧的滚动条将消失。

现在注释掉 RenderTransform 并取消注释 LayoutTransformControlLayoutTransform - 将遵守布局规则,并且 VerticalHorizontal 滚动条将保留在所有涉及的平台上的预期位置。

Windows

Linux

浏览器

Android

Avalonia 提供的底层图形功能

Avalonia 就像 WPF 和 Silverlight 一样,通过其每个面板或控件的 DrawingContext 提供底层图形功能,并且此功能在 Avalonia 覆盖的所有平台上都相同。这种访问对于在需要快速更改控件时(例如,显示股票买卖的 DataGrid)提高性能非常有用。

据我所知,Uno Platform 和 MAUI 都不提供此类功能。

展示这种底层功能的示例称为 NP.Demos.DrawingContextSample。它通过重写 MainView 控件的 Render 方法来绘制一个带蓝色边框的红圈。

public override void Render(DrawingContext context)
{
    context
        .DrawEllipse
        (
            Brushes.Red,
            _strokePen,
            new Avalonia.Point(100, 100),
            40,
            40);
}

Windows

Linux

Web 浏览器

Android

WinUI 中未提供的 Avalonia API 示例(以及相应地 Uno Platform 或 MAUI 中未提供的)

此部分可能需要对 WPF 和 XAML 编程有所了解。

为了避免显示大量代码,我将只使用小型代码片段,但其中大部分可在 NP.Demos.GenericTypesInXamlNP.Demos.StaticMarkupExtensionSample 示例中找到。

以下是 WinUI(以及相应地 Uno Platform 或 MAUI)中缺失的重要 API 列表:

  1. 逻辑树的概念。
  2. 绑定到视觉树或逻辑树上某个元素的属性(RelativeSource 模式为 AncestorType)。CommunityTookit 提供了一种方法来实现这一点,该方法在大约 70% 到 80% 的情况下有效,但有时无效。例如,我从未成功地从 DataGrid 的单元格或行绑定到 DataGrid 本身的属性。
  3. 可以向下继承到视觉树的自定义附加属性和依赖属性。
  4. MultiBinding - 我的客户自己实现了它,但有时也无法工作,例如双向多重绑定。
  5. x:Static 标记扩展。如果希望绑定到 static 属性等,它很重要。
  6. 绑定中缺少 StringFormat
  7. 样式设置器不允许绑定其值。
  8. XAML 中的泛型类型或 x:Array 标记扩展。

逻辑树的概念

WPF 和 Avalonia 采用了比视觉树更稀疏但包含大部分所需信息的逻辑树概念。通常更容易操作逻辑树,因为它节点数量较少,并且与 XAML 非常匹配。WinUI(或 UWP)从未有过这个概念。

使用 AncestorType 模式的 RelativeSource

Avalonia 允许您在 XAML 中绑定到视觉树或逻辑树上方的属性,例如:

<Grid Tag="Hello World">
    <TextBlock Text="{Binding Path=Tag, 
               RelativeSource={RelativeSource AncestorType=Grid}}"
               HorizontalAlignment="Center"
               VerticalAlignment="Center"/>
</Grid>  

TextBlockText 属性绑定到包含它的 GridTag 属性。

对于逻辑树上元素上定义的属性,甚至有一个快捷方式

Text={Binding $parent[Grid].Tag}

为什么它很重要 - 很多时候您需要绑定到树(通常是逻辑树,较少是视觉树)上方的某个属性。例如,DataGrid 内单元格的背景可能取决于 DataGridDataContext 上定义的某些属性,而单元格无法访问(它只能访问单元格的 DataContext)。

属性值沿视觉树或逻辑树继承

此功能与上述功能相关 - 有时,您希望整个属性向下传播到视觉树或逻辑树(除非另有指定)。一个很棒的内置属性示例是 DataContext。WinUI 能够传播 DataContext 依赖属性,但不允许自定义依赖属性和附加属性继承。

WPF 和 Avalonia 都允许这样做。Avalonia 在其 RegisterAttached(...) static 方法中有一个 inherit 参数。

为什么它很重要 - 例如,很多时候 DataContext 被用于其他功能,例如在第三方控件中,而您想创建自己的自定义数据上下文来连接应用程序的各种视觉部分。Avalonia 和 WPF 允许这样做,而基于 WinUI 的框架则不允许。

MultiBinding

当您希望一个视觉属性依赖于多个其他属性并在其中任何一个发生变化时进行更改时,MultiBinding 很重要。WinUI 没有内置的 MultiBinding 功能,而 WPF 和 Avalonia 都支持。

此外,WPF 不允许递归多重绑定,而 Avalonia 则允许 - 这意味着某些多重绑定源可以作为另一个多重绑定的结果进行计算。

x:Static 标记扩展

Avalonia 和 WPF 都提供它,而 WinUI 则取消了它。

x:Static 在您希望轻松地从 WPF 访问 C# 中定义的 static 属性时非常方便。

例如,如果您有一个无状态的 Binding Converter,您可以在 Converter 类中创建该类型的 static 实例属性,并从引用该转换器 DLL 的项目中的每个 XAML 的每个绑定中访问它。而在 WinUI 中,您已将所有 Converter 定义在某个 XAML 资源文件中的资源,这是一个额外的、不必要的步骤,并且经常导致资源重复。

x:Static 扩展的另一个绝佳用途是用于静态行为。例如,您正在构建一个行为,当某个视觉元素触发 PointerPressed 事件时调用某个方法。如果您想使该行为通用,以触发开发人员指定的任何路由事件 - 最佳方法是通过(通过 static 附加属性)将行为连接到定义为 Static 属性的 RoutedEvent 属性。例如:

 <Grid myProj:MyBehavior.TheEvent="{x:Static InputElement.PointerPressedEvent}"/>

StringFormat

WinUI 不允许在其绑定中使用 string 格式,而这是一项非常有用的功能,可以简化 XAML 代码。

<TextBlock Text="{Binding Path=Value, StringFormat='Value={0}'}"/>

例如,如果绑定值等于 5,则 TextBlock 将显示:

Value=5  

这样就可以实现,而无需绑定转换器或多个 TextBlock

具有绑定的样式设置器值

Avalonia 和 WPF 允许将 Style Setter 的值绑定到视觉和非视觉对象的各种属性。例如:

<Style x:Key="MyStyle">
    <Setter Property="Background"
               Value="{Binding Path=MyBrushDefinedInViewModel}"/>
</Style>  

WinUI 不允许这样做。

为什么它很重要 - 因为它提供了一种自定义控件的好方法。例如,如果您希望样式中定义的某个控件模板根据某个值进行自定义 - 您可以在样式上创建一个相应的依赖属性或附加属性,在 ControlTemplate 中绑定该属性,并将 Style 属性绑定到所需变量。不幸的是,这在 WinUI 中是不可能的,您必须复制粘贴带有控件模板的样式才能达到相同效果。

在 Avalonia(和 WPF)中,生成的 XAML 代码比基于 WinUI 的产品更具重用性。

XAML 中的泛型类型

据我所知,Uno 和 MAUI 都不允许在 XAML 中使用泛型类型,尽管 x:Array 是 XAML 规范的一部分。

WPF 在泛型类型方面也有问题 - 它只允许在 XAML 文件的根元素上使用。

然而,Avalonia 在 XAML 中完全支持泛型类型。

例如,您可以按如下方式在 XAML 中定义一个 string 列表:

<UserControl xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:generic_collections="clr-namespace:System.Collections.Generic;
             assembly=System.Collections"
             x:Class="NP.Demos.GenericTypesInXaml.Views.MainView">
    <UserControl.Resources>
        <generic_collections:List x:Key="TheList"
                                  x:TypeArguments="x:String">
            <x:String>Str1</x:String>
            <x:String>Str2</x:String>
            <x:String>Str3</x:String>
        </generic_collections:List>
    <UserControl.Resources>
</UserControl> 

如果您有一个 Generic 类,例如:

public class MyGenericClass<T1, T2>
{
    public T1? Val1 { get; set; }

    public T2? Val2 { get; set; }
}  

您可以按如下方式在 Avalonia 资源中定义该类型的对象:

<views:MyGenericClass x:Key="MyInstance"
                      x:TypeArguments="x:Int32, x:String"
                      Val1="1"
                      Val2="Hello World 1"/>  

最后,MyGenericClass 类型对象的列表可以在 XAML 中定义为:

<generic_collections:List x:Key="ListOfGenericClasses"
                            x:TypeArguments="views:MyGenericClass(x:Int32, x:String)">
    <views:MyGenericClass x:TypeArguments="x:Int32, x:String" 
                            Val1="2"
                            Val2="Hello World 2"/>
    <views:MyGenericClass x:TypeArguments="x:Int32, x:String"
            Val1="3"
            Val2="Hello World 3"/>
</generic_collections:List>  

结论

Avalonia 是一个伟大的跨平台框架,它扩展了其 API 和平台,以便在桌面平台之上覆盖 Web 和移动。

它提供了许多新功能,并且可以称之为Multiplatform WPF++。

在我看来,它比实现 WinUI 功能的竞争对手更好,并且更接近 WPF。

如果您的公司对 Multiplatform XAML/C# 解决方案感兴趣,Avalonia 绝对值得认真考虑。

致谢

特别感谢我的同事兼朋友 Jon Sinsel,他向我指出了 WinUI 依赖属性的性能问题以及详细说明这些问题的链接。 

历史

  • 2023 年 8 月 20 日:初始版本
© . All rights reserved.