多平台XAML/C#奇迹包:Avalonia。将Avalonia与基于WinUI的解决方案进行比较
本文介绍了Avalonia作为Web和移动开发平台的新多平台XAML/C#解决方案,该解决方案基于其已有一段时间可用的桌面功能。将Avalonia的各种API和视觉功能与其竞争对手的框架进行了比较,并且Avalonia在多个平台上比其竞争对手更强大、更统一。
目录
Microsoft UI编程天堂中的麻烦——应用程序部门攻击
Visual Avalonia多平台功能示例在跨多个平台的其他框架中不可用
Avalonia API示例在WinUI中不可用(在Uno平台或MAUI中相应地)
将RelativeSource与AncestorType模式配合使用
免責聲明
在本文中,我只提出我个人的观点。
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平台WinUI实现适用于桌面(Windows、Linux和MacOS)、Web(通过WASM)和移动设备(Android、iOS和Tizen)
- MAUI WinUI实现适用于桌面(Windows和MacOS)和移动设备(Android、iOS 和Tizen)——不适用于Web或Linux
从上表中,您可以看到MAUI与Avalonia和Uno相比的第一个缺点——它不允许产品在Web上运行,并且它不提供适用于Linux的桌面解决方案。
我对Microsoft的态度
在这篇文章中,我将对最近的Microsoft UI包说一些严厉的话,所以我想澄清的是,我和微软在意识形态上没有任何重大分歧,就像理查德·斯托曼那样。我只是将各种软件包判断为使用它们来构建产品的人。
除了他们目前的UI部门之外,我实际上很钦佩Microsoft,因为它开发并共享了C——我认为软件语言中最好,最强大,发展最快的软件开发语言。此外,他们使大部分C#(不包括UI)在包括Linux和Mac在内的多个平台上运行,并在限制最少的MIT许可证下开放其源代码。
Microsoft还率先开发了各种编程范式,例如LINQ、Tasks(Promises)和Reactive Extensions,并在将它们合并到其他语言之前将它们合并到C#中。
Microsoft创建了当时最好的Windows开发框架——WPF,并提出了多个UI开发概念,包括MVVM模式——UI开发的最佳模式。
Microsoft创建了最好的Web开发框架——ASP.NET并不断努力改进它。
Microsoft创建了一个强类型版本的JavaScript——TypeScript,如果他们选择HTML/JavaScript作为他们的开发平台,我会推荐给任何使用JavaScript的人——HTML/JavaScript开发团队越大,对TypeScript的需求就越大。
Microsoft创建了WSL——Window Subsystem for Linux,使得在Windows上开发和调试Linux UI和控制台应用程序变得轻而易举。
最重要的是,Microsoft目前在许多领域都处于领先地位,包括他们的云服务(Azure)、人工智能、嵌入式系统等。
我的背景和与Avalonia、Uno和MAUI的关系
我是顶级的WPF/MVVM架构师和开发人员之一。
几年前,我发现了Avalonia,它从第一眼就爱上了它。
在这篇文章中,我将说很多关于Avalonia的好话,而对其他框架则不那么好,所以我想强调的是,除了
- 一见钟情
- 构建多个基于Avalonia的开源库
- 在CodeProject上撰写多篇关于Avalonia的文章
- 几年前帮助他们编写文档,并为此获得了总计2500美元(也是几年前)
- 在2年的时间里,我为我的几份合同使用Avalonia桌面解决方案。
- 现在,在2023年底——在文章最初发表很久之后,我已经成为了Avalonia MVP
Avalonia没有赞助我,他们在这篇文章发表之前并不知道我写了这篇文章。
Uno平台
在我的一份合同中,我已经广泛使用Uno Platform大约6个月,用它构建了一些复杂的控件,包括甘特图控件和模仿并提供大量Excel电子表格功能的控件。我还在开发和维护一个复杂的、成熟的、基于Uno平台的应用程序,它是旧WPF应用程序的端口。
在与Uno合作的过程中,我主要专注于桌面和Web平台。
MAUI
我从未在我的任何合同中使用过MAUI,只是在我写这篇文章时玩过它。
这篇文章的内容可以信任吗?
我认为——是的,因为我所说的几乎所有内容都得到了具体样本的支持,尽管我自称热爱Avalonia,但我正在努力保持尽可能客观。
有时,我会使用猜想,但在这种情况下——我会特别声明这只是我的猜测。
文章的主要结论
可以使用Avalonia、Uno和MAUI,我自己也看到了使用Avalonia和Uno平台创建的产品。至于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#框架的体系结构
与 UNO Platform 和 MAUI 相比,Avalonia 有两个优势:
- Avalonia并不局限于WinUI/UWP功能——事实上,Avalonia在许多方面比WPF强大得多——它是多平台WPF++,而WinUI(和UWP)API是对WPF API(WPF--)的苍白模仿。我将在下面详细介绍它,详细介绍WinUI缺少的一些API,并提供具体示例。
- Avalonia的设计理念是为不同的平台重复使用尽可能多的代码。他们在Skia之上构建了他们的框架——这是一个很棒的低级多平台框架。无论他们不能使用Skia做什么,他们都可以通过平台特定的解决方案来实现。UNO和MAUI的理念恰恰相反——他们为每个平台使用尽可能多的原生代码,并且对于他们所涵盖的每个平台,它们的实现完全不同,因为他们试图使用尽可能多的原生平台功能。
这是Avalonia架构图。
Polyfill是一个通用的API(接口),具有依赖于平台的实现。如您所见,特定于平台的代码仅占Avalonia代码总数的一小部分,而每个平台的大部分代码都是相同的。
以下是Uno平台的架构图:
每个实现都有完全不同的代码——事实上,Web实现生成HTML,而Windows实现使用WinUI本机调用;Linux——Skia和Mobile以及MacOS实现正在使用Xamarin。因此,Uno被迫维护其代码的4个不同版本,并且可用的功能和行为在不同的平台上是不同的——我非常了解它,并将在下面详细讨论它。
这是MAUI架构图:
MAUI覆盖的平台较少,但存在与Uno相同的缺点:
- 它尝试实现WinUI(即WPF--)
- 它尽可能多地使用原生代码——因此每个平台基本上都有自己的实现。
WASM游乐场
Avalonia和Uno都有Web(WASM)游乐场,您可以查看——Avalonia Playground和Uno Playground
MAUI没有Web 游乐场(因为没有MAUI Web解决方案)。
Microsoft的XAML/C#开发简史
WPF
大约20年前,一群在Microsoft的UI开发方面拥有丰富经验的非常聪明的人提出了一个很棒的新.NET包,用于在Windows上进行桌面开发。该项目最初被称为Avalon,然后被命名为WPF(Windows Presentation Foundation)。
为什么WPF很棒?因为:
- 它的低级功能允许在Windows上创建和自定义几乎任何UI,以完全符合客户的要求。
- 它带有许多高级功能,代表了新发现和正式化的UI概念,从而实现了关注点和重用的分离。特别:
- WPF将Composition的概念提升到了一个新的水平——几乎任何简单的控件都可以从基本的基元(如边框、面板、形状、图像和文本)组装而成。
- 反映WPF UI控件的组合层次结构的可视化树和逻辑树。
- Dependency和Attached属性,用于节省内存并允许在不修改元素类的情况下将值附加到WPF UI元素上的新属性。
- 允许将目标属性绑定到源属性的绑定。WPF UI元素上的几乎每个属性都是可绑定的。与DataTemplates一起绑定最终产生了MVVM模式,它允许可视代码和非可视代码完全分离,以这样的方式,非可视代码包含大部分复杂性,而可视代码被动地模仿非可视代码。
- 路由事件(也可以是附加路由事件),在可视化树中上下传播。例如,包含的元素可以触发此类事件,但它将在包含元素上处理。
- 控件模板——允许重用XAML的某些部分。
- DataTemplates——允许将一些非视觉数据转换为视觉对象。
- 样式——允许轻松重新设置WPF控件的样式。样式也可以从其他样式继承。
- 模板和样式触发器允许轻松修改行为。
- 它允许使用一些低级图形机制来构建更快的控件。
它还包含许多其他不太重要的概念和改进,我在这里省略了这些概念和改进。
- WPF与MS Visual Studio(XAML智能感知和设计器)以及名为Snoop的最佳开源间谍工具进行了很好的集成。
许多聪明的人发现了WPF的潜力,立即加入了WPF的行列,并创建了一个名为 WPF Disciples 的互联网社团。我可以说出卡尔·希夫莱特、乔什·史密斯、彼得·奥汉隆、萨沙·巴伯、比阿特丽克斯·科斯塔、罗伯·艾森伯格的名字。在这一点上,还有许多其他人的名字我不记得了。
在Microsoft放弃了Silverlight并将WPF放在次要位置,取而代之的是可怜的替代项(UWP和WinUI)之后,这些人中的许多人转向了其他(非Microsoft)技术。
现在,想想互联网社团的名字——WPF Disciples。创建这个互联网社会的人明白,WPF不仅仅是另一个用于可视化开发的软件包。他们明白WPF是一种新的开发理念——本质上是一套非常深入和有用的新范式,允许更快的开发速度,并创建更精简、更易于维护和扩展的代码。此外,许多WPF功能也可以用于非Visual开发(尽管我认为它们目前还没有在Visual开发之外被广泛使用)。
有些人抱怨WPF太复杂了,无法学习。我的回答是,WPF并不比可视化编程更难。WPF没有很多不需要的功能——那些优秀的可视化程序员不需要知道的功能。
WPF肯定有一个学习曲线,但是一旦你了解并理解了WPF,你就可以非常快速和精益地编写非常复杂的产品。
基本上,WPF有一定的进入壁垒,但一旦你了解并理解了它,你就会喜欢它,它会给你带来巨大的开发能力。
Silverlight(幸福的记忆)
WPF有一个主要缺点——它不是多平台的。正因为如此,在某个时候,Scott Guthrie领导下的Microsoft部门创建了Silverlight(以前称为WPF)。
Silverlight有许多(但不是全部)WPF功能——我经常遇到一些怪癖,例如,自定义附加属性不能动画处理的奇怪限制等等。然而,它有一个很大的优势:它(连同它的.NET框架版本——它也不是全功能)可以在多个平台上运行。
起初,Silverlight是作为大多数流行浏览器的浏览器插件发布的,但后来MS也提供了桌面版本。
Microsoft UI编程天堂中的麻烦——应用程序部门攻击
说到上面的图片——我不知道哪个Microsoft部门是那里的反派,哪个是好人——我认为每个人都是两者兼而有之。
显然,Microsoft的Windows和Application部门之间存在一些紧张关系。Windows部门主要使用C++和应用程序部门——主要使用C#。
受到C#和WPF/Silverlight成功的鼓舞,.NET C#专家希望在.NET Framework中重写整个Microsoft操作系统。无论是由于Windows部门的合作不力还是由于其他一些原因,但这种努力基本上导致了MS Vista操作系统在公司和开发人员中根本不受欢迎。
Microsoft的更多麻烦(Windows部门反击)
大约在2010-2011年,Microsoft意识到不建立自己的移动平台是一个错误。到那时,苹果和谷歌由于在智能手机/平板电脑市场的主导地位,在市值上遥遥领先于Microsoft。
苹果是第一个创建移动平台的公司(同时,他们锁定了他们的操作系统)。谷歌排在第二位,但由于谷歌的产品更便宜,而且谷歌的Android系统是开源的,最终谷歌占据了超过一半的移动市场份额。
虽然Microsoft正确地发现了问题,但它(无疑是在他们赚钱的Windows部门的影响下)得出了以下所有错误的结论:
- C++应该是编写大多数应用程序的语言。(C++的编译速度非常慢,因此与C#或Java相比,它不是应用程序开发的好语言)。
- HTML5/JavaScript足以用于多平台应用程序开发。(无论是TypeScript还是任何新的JavaScript框架——Angular、Vue或React——在功能、开发速度和易维护性方面都无法与WPF相提并论)。
- 因此,Silverlight/WPF和C#应该被放弃或接近放弃。
- 人们应该放弃在台式机和笔记本电脑上启动Windows应用程序的“开始”按钮(这是一个非常荒谬的按钮,因为台式机/笔记本电脑和移动用例完全不同,每个用例都应该区别对待并使用不同的UI)。
- Microsoft应该像苹果一样努力关闭他们的系统。(在移动市场的第三阶段,他们试图表现得像市场领导者一样)。
这种态度实际上导致浪费了数十亿美元来创建自己的移动平台和硬件——Windows RT,无法运行.NET Framework和C#。Windows RT(作为独立的操作系统)和Microsoft移动硬件最终由于对它缺乏兴趣而被埋没,这反过来又是由以下原因引起的:
- 缺乏应用程序开发——开发人员不喜欢新平台。
- 非开源操作系统(例如,像Android一样)和人们担心将被完全关闭,因此只有Microsoft才能决定允许哪个应用程序,哪个不允许——有点像Apple对Silverlight移动版本所做的那样。
- 对Microsoft下一步将要做的事情缺乏信心。
- Microsoft 8 操作系统的失败——这主要是由于缺少开始按钮和对客户投诉的充耳不闻。
妥协
最终,MS应用程序开发人员说服Microsoft至少在一段时间内保持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,要么切换到非Microsoft技术。
举个例子:经过Microsoft推广UWP超过10年和推广WinUI几年之后,骰子上没有显示一个UWP或WinUI作业,而有120个WPF作业(这是我写这篇文章的今天——当然,数字可能每天都在变化,但它们通常保持接近)。
显然,Microsoft的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 for Desktop已经过许多使用Avalonia构建的产品的全面测试,这些产品列在Avalonia的网站上。
Uno平台
Uno Platform由Uno公司开发,其目的也是提供多平台桌面、Web和移动解决方案。它与Microsoft密切相关——它的几位创始人是Microsoft的MVP。 相应地,它遭受了与Microsoft产品相同的缺点。
从本质上讲,Uno平台为Windows、Linux、Mac、Web和移动平台提供 WinUI(或旧版本——UWP)实现。
此外,在Xamarin和MAUI示例之后,Uno作为本机平台调用的包装器实现。这导致了完全不同的代码,例如,用于Windows、Linux、移动和Web。事实上,虽然Uno for Windows是作为Windows本机调用的包装器实现的,但其Web实现依赖于生成HTML/JavaScript代码。
因此,在Windows平台上运行的功能在Web上可能不同。事实上,在使Windows工作之后,必须切换到Web并使相同的功能也在其上运行。这可能需要一些时间来弄清楚如何去做。错误也是如此:在Windows上运行的东西可能会在Web上崩溃,反之亦然。
下面我就再说一些具体的案例。
平均而言,在对Uno进行编程时,我每周花费大约6-8个小时使Web版本的行为类似于Windows版本。
对于Avalonia——这样的时间接近0。
MAUI
MAUI是Xamarin的新版本,还具有创建桌面应用程序的一些功能。到目前为止,我从未见过自己使用MAUI构建的桌面应用程序,并且在一些论坛上,关于将其用于桌面应用程序的意见通常是负面的。
它遇到了与Uno相同的问题,同时覆盖的平台更少。
示例的一般说明
如上所述,Avalonia是WPF++,而WinUI(正在取代UWP)是WPF--。此外,由于Avalonia架构,为一个平台开发的功能也更有可能在另一个平台上工作。
此外,与MAUI或Uno Platform相比,Avalonia具有更好的开发工具和更快的大型项目编译时间。
相应地,比较分为以下几组:
- 开发工具和调试功能的比较——仅口头表达,没有代码示例。
- 代码示例,证明Avalonia具有WinUI中缺少的所有重要功能(以及Uno和MAUI中相应缺失的功能)。
请注意,为了理解示例的代码,我希望人们对XAML/C#框架(最好是WPF)有一定的了解。
那些不了解WPF或其他XAML/C#包的人可以跳过代码,只阅读文本。
涵盖的平台、开发工具、第三方软件包和调试功能的比较
开源
所有三个框架——Avalonia、Uno Platform和MAUI都是开源的,并在最宽松的MIT许可下发布。这意味着你基本上可以用代码和包做你想做的事,在任何商业或非商业产品中使用它,只要你提到原始代码来自相应的包。
这三个包的源代码都位于Github上。以下是位置:
涵盖的平台
如上所述,Avalonia和Uno Platform涵盖桌面、Web的Windows/Linux/MacOS和移动的Android/iOS/Tizen。
MAUI——不为Linux桌面或Web提供解决方案——从某种意义上说,它在这里处于劣势。
第三方控制
复杂的第三方控制
另一个重要问题是第三方的可用性,特别是对于实现一些复杂的想法,例如,Desktop Docking/Grouping/Tabbing控件、MS Excel、报表、PDF或布局管理控件。除了我自己的Avalonia UniDock Docking/Grouping/Tabbing开源解决方案之外,Avalonia和Uno和MAUI都没有为他们构建如此复杂的控件,我仍然需要升级该解决方案才能与Avalonia 11一起使用。
由知名零部件公司开发的更简单的控件
当涉及到更简单的控件时,MAUI具有明显的优势,因为它背后的Microsoft名称——DevEx,Telerik和其他主要公司正在为MAUI开发控件,尽管在这一点上,这些控件并不像WPF那样复杂,并且主要服务于移动设备,而不是新的MAUI桌面解决方案。到目前为止,这些由大牌提供的简单控件中的大多数都可以在Avalonia和Uno中轻松创建和设置样式。
DataGrid和Tabs控件
Avalonia和UNO Platform都将DataGrid和TabControl为其源代码的一部分(用于Avalonia)或作为开源包的一部分(用于UNO平台)。
MAUI为DataGrid内置了TabBar和一些商业解决方案,例如Telerik或SyncFusion。
图表
基于Open GL构建的几个图表具有兼容性(至少对于桌面和Web Avalonia软件包而言)。特别是,OxyPlot、LiveCharts和 ScottPlot 可与Avalonia配合使用。
还有一个商业解决方案——Tee Chart。
Telerik和Syncfusion具有用于MAUI的商业图表组件。
Syncfusion还具有Uno Chart控件——在测试版中。
即将推出
Actipro Controls for Avalonia(Avalonia的Actipro控制)。
产品速度
这是我的主观观点,即Avalonia产品在多个平台上通常比Uno更快。
一个例外是——浏览器滚动中的Avalonia DataGrid控件与相应的Uno控件一样慢(均在AOT编译下)。
Uno和MAUI都是作为Windows上的WinRT本机调用的包装器构建的。WinRT互操作存在几个众所周知的问题,这使得WinUI依赖项属性的性能非常慢。此问题在以下链接中进行了详细说明:WinUI 3性能提升。
此外,github问题链接:“WinUI 3中的依赖项属性比WPF慢得多”显示,Microsoft自2019年11月以来就知道这个问题,但尚未采取任何措施。
间谍工具
WPF Snoop
WPF有一个很棒的UI间谍工具——Snoop。它允许:
- 显示可视树或逻辑树
- 在视觉或逻辑树中快速查找元素
- 检查和修改元素的Attached和Dependency属性
- 迭代钻研(向下钻取)属性——允许检查化合物属性的值
- 检查元素的DataContext并迭代钻研其属性
- 查看事件传播——在可视化树中向下——用于隧道事件,在可视化树中查看冒泡事件。这非常重要,例如,当您想弄清楚为什么某些事件没有到达可视化树上的元素时。
- 对任何元素或public属性调用public方法。
Avalonia和UNO以及MAUI都没有像WPF那样强大的间谍工具。然而,Avalonia拥有三者中最好的间谍工具:
Avalonia间谍工具
Avalonia Spy Tool的强大功能远不如Snoop(在这一点上):
它允许:
- 显示可视化或逻辑树
- 快速找到这些树中的元素
- 检查和修改元素上的附加属性和依赖项Avalonia属性
- 监视可视化树中上下路由事件的传播
- 深入研究(向下钻取)化合物属性,但不显示有关其结合的信息
它不允许:
- 显示有关绑定的详细信息
- 对元素和属性调用方法
Uno和MAUI
Uno和MAUI只允许在其上使用Microsoft Visual Live Tree和Live Properties,这比Avalonia Spy Tool更糟糕,因为
- 仅当在Visual Studio中调试应用程序时,它才有效。
- 它仅显示视觉树(WinUI中没有逻辑树的概念)。
- 它不允许查看路由的事件传播。
- 它跳过了一些元素,尤其是在复杂的控件上,例如DataGrid。
- 对于某些控件,它没有显示它们的属性——我没有弄清楚那里的任何规则,但通常它发生在一些复杂控件的某些部分,例如,DataGrid。
- 通常,它不允许无正当理由修改属性,因为此类属性是public和可设置的。
它提供了两个不属于Avalonia Spy Tool的功能:
- 可以立即获取在实时可视化树中找到的元素的代码
- 显示有关绑定的信息
尽管有这些额外的功能,但我发现使用Avalonia Spy Tool比使用Live Visual Tree/Live Properties组合要好得多,而且WPF Snoop远远超过了这些工具中的每一个。
用于运行比较示例的Visual Studio版本
我使用Visual Studio 2022 17.7预览版2.0。
这并不意味着您一定需要相同的版本——对于大多数示例,VS 2022版本无关紧要。
用于示例的Avalonia框架
Avalonia 11.04
有关示例的信息
通常,示例分为两组——可视化示例和API示例。也没有提供Avalonia在所有平台上可用的功能的详尽列表,而在其竞争对手的框架中不可用,但我试图列出最重要和最令人印象深刻的功能列表。
示例简介
为了扩大本文的受众,我将介绍一些示例,而不讨论太多代码,即使本文的所有代码都可用,并且可以在 Avalonia比较示例存储库中找到。
以下所有示例均已在Windows 11、Linux(通过 WSL)、Chrome Web浏览器和Android模拟器上进行了测试。
为了在Linux上运行桌面示例——请阅读我的文章——在适用于Linux的Windows子系统(WSL)上运行和调试多平台.NET(.NET Core、.NET5和.NET6)GUI和控制台应用程序
在这一点上,我没有能力或时间在MacOS、iOS和Tizen平台上运行示例。
下面的所有示例在Avalonia中运行良好,但无法在Uno平台或MAUI上运行,无论是在相应示例中提到的每个平台上还是在某些特定平台上。
Visual Avalonia多平台功能示例在跨多个平台的其他框架中不可用
模糊页面示例
有时,您希望向客户预览某些功能,而不让客户使用它们。采用的做法之一是使用模糊效果让客户看到一些文本和按钮,而无法辨认出其中所写的内容。WinUI具有模糊效果,因此可以在Windows的Uno平台上使用它,但在Uno Web版本中缺少这种效果,因为Uno Web是通过生成HTML/JavaScript获得的,并且浏览器无法模糊其HTML树的某些部分。
为了模糊大的特征,可以使用Acrylic笔刷——但它不适合模糊小特征,例如文本,并且仍然允许看到那里写了一些东西。
Avalonia Blur示例的代码位于项目NP.Demos.BlurSample下。
以下是每个平台的模糊页面的图像:
Windows桌面:
Linux桌面(通过WSL):
浏览器中的模糊:
Android:
创建和运行多平台Avalonia项目
只有想要自己运行示例和/或使用Avalonia进行开发的人员才需要此部分。
若要使用Avalonia 11创建多平台项目,需要为Visual Studio 2022安装Avalonia Template Studio扩展:
创建新项目时,从Template Studio中选择“Avalonia C#项目”:
在下一个屏幕上,选择项目位置和名称,然后按“创建”按钮。以下是您将看到的内容:
选择您希望应用程序运行的平台,然后按底部的“创建”按钮。
它将创建一个可重用的项目和多个项目,以在每个平台上运行应用程序:
在BlurSample解决方案中,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]支持RenderTargetBitmap。
AFAIK MAUI允许在其覆盖的每个平台上截图。
在所需的平台上打开并启动NP.Demos.ElementToImage项目。按底部的渲染按钮和包含TextBlock,Rectangle的顶部元素,并且Combobox将在下面的3个不同行中渲染——最后两行带有平铺(尽管可以关闭平铺)。
以下是生成的图像:
Windows:
Linux:
浏览器:
Android:
为了简洁起见,我在这里没有详细说明代码,但唯一的更改是在文件MainView.axaml和MainView.axaml.cs中。
前两行图像是在RenderTargetBitmap转换为Bitmap的帮助下实现的。第一个图像行显示一个Image控件,其Source属性设置为Bitmap。
第二个图像行是用一个Panel实现的,背景是用ImageBrush创建的。
第三行图像表示创建失控图像的最简单方法——通过VisualBrush。请注意,与WPF不同,VisualBrush它不会自动反映对控件的更改。
三维示例
我从Avalonia源代码Render Samples中复制了下一个示例的代码,并使其在不同的平台上运行。请在NP.Demos.Complex3DSample解决方案中找到它。
Avalonia允许在Rotate3DTransform的帮助下在多个平台上运行一些3D功能,而AFAIK Uno和MAUI都不允许。下面是运行示例的结果:
Windows:
Linux:
浏览器:
Android
裁剪和不透明度蒙版示例
AFAIK、剪裁和不透明度遮罩均未在Uno平台(Windows桌面平台除外)上实现。对于MAUI,实现了剪裁,但未实现不透明度蒙版。
Clipping/OpacityMask示例位于NP.Demos.ClippingSample解决方案中:
Windows:
Linux:
浏览器:
Android:
LayoutTransformControl示例
对于CommunityToolkit中的Uno 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>
滑块的值是TwoWay绑定到视图模型上的ScaleFactor,并从1到5变化。
首先——让我演示LayoutTransformControl为什么需要,为什么我们不能使用RenderTransform。
注释掉LayoutTransformControl的ScaleTransform,取消注释DataGrid.RenderTransform的ScaScaleTransformleTransform。
现在,必须将DataGrid放入分配的空间的布局规则将不会被观察到,并且当您通过移动滑块稍微增加ScaleFactor时,右侧的滚动条将消失:
现在注释掉RenderTransform并取消注释LayoutTransformControl的LayoutTransform——将遵守布局规则,并且Vertical滚动条和Horizontal滚动条都将保留在每个涉及的平台上应该的位置:
Windows:
Linux:
浏览器:
Android:
Avalonia提供的低级图形功能
像WPF和Silverlight一样,Avalonia通过每个面板或控件的DrawingContext提供低级的图形功能,这种功能在Avalonia覆盖的所有平台上都是一样的。当人们需要快速变化的控制时,这种访问对于更好的表现非常有帮助——例如,DataGrid反映股票的买卖。
AFAIK 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:
浏览器:
Android:
Avalonia API示例在WinUI中不可用(在Uno平台或MAUI中相应地)
本部分可能需要对WPF和XAML编程有一定的了解。
为了避免显示大量代码,我将只使用小代码片段,但其中大多数代码片段在NP.Demos.GenericTypesInXaml和NP.Demos.StaticMarkupExtensionSample示例中可用。
下面是WinUI中缺少的重要API列表(以及相应的Uno平台或MAUI中):
- 逻辑树的概念。
- 绑定到视觉树或逻辑树上元素的属性(带AncestorType模式的RelativeSource)。CommunityTookit提供了一种在~70%到80%的情况下有效的方法,但不幸的是,有时它不起作用。例如,我永远无法从DataGrid单元格或行绑定到DataGrid本身的属性。
- 自定义附加属性和依赖属性,它们可以沿向可视化树继承。
- MultiBinding——我的客户端自己实现了它,但在某些情况下它不起作用,例如双向多重绑定。
- x:Static标记扩展名。例如,如果想要绑定到static属性,这一点很重要。
- StringFormat在绑定中缺失。
- 样式设置器不允许绑定其值。
- XAML中的泛型类型或x:Array标记扩展。
逻辑树的概念
WPF和Avalonia采用了逻辑树比视觉树更稀疏的概念,但包含大部分所需的信息。使用逻辑树通常更容易操作,因为它具有较少的节点数,并且与XAML非常匹配。WinUI(或UWP)从未有过这个概念。
将RelativeSource与AncestorType模式配合使用
Avalonia允许绑定到XAML中可视化或逻辑树上更高的属性,例如:
<Grid Tag="Hello World">
<TextBlock Text="{Binding Path=Tag,
RelativeSource={RelativeSource AncestorType=Grid}}"
HorizontalAlignment="Center"
VerticalAlignment="Center"/>
</Grid>
将TextBlock的Text属性绑定到包含它Grid的Tag属性。
对于在逻辑树上定义的元素的属性,甚至还有一个快捷方式:
Text={Binding $parent[Grid].Tag}
现在为什么它很重要——很多时候,您需要绑定到其中一个树上定义的属性——通常在逻辑树上,很少在可视化树上。例如,DataGrid中单元格的背景可能取决于在DataGrid的DataContext上定义的一些属性,单元格无法访问这些属性(它只能访问单元格的DataContext)。
沿着可视化树或逻辑树继承属性值
此功能与上面的功能相关——有时,您希望整个属性向下传播到视觉或逻辑树(除非另有说明)。这种内置属性的一个很好的例子是DataContext。WinUI连接DataContext依赖项属性的传播,但不允许继承自定义依赖项和附加属性。
WPF和Avalonia都允许这样做。Avalonia的RegisterAttached(...) static方法有一个inherit参数。
为什么它很重要——例如,很多时候DataContext忙于一些其他功能,例如,在第三方控件中,并且您想要创建自己的自定义数据上下文来连接应用程序的各个可视部分。Avalonia和WPF允许它,而基于WinUI的框架则不允许。
多重绑定
MultiBinding当您希望视觉属性依赖于多个其他属性并在其中一个属性发生更改时进行更改时,这一点很重要。WinUI没有内置MultiBinding功能,而 WPF 和 Avalonia 都有。
此外,WPF不允许递归多重绑定,而Avalonia允许,这意味着某些多重绑定源可以作为另一个多重绑定的结果进行计算。
x:static标记扩展
Avalonia和WPF都提供了它,而WinUI拒绝了它。
当您想在C#中定义static属性并从WPF轻松访问它时,x:Static非常方便。
例如,如果你有一个无状态绑定转换器,则可以在Converter类中创建该类型的static实例属性,并从引用转换器DLL的项目中每个XAML中的每个绑定访问它。相反,在WinUI中,你已将所有转换器定义为某个XAML资源文件中的资源,这是一个额外的不必要的步骤,通常会导致资源重复。
x:Static扩展的另一个重要用途是静态行为。例如,您正在构建一个行为,该PointerPressed行为在可视元素上发生事件时调用某个方法。如果要使Behavior泛型以触发开发人员指定的任何路由事件——将行为(通过static附加属性)连接到定义为Static属性的RoutedEvent属性的最佳方式。例如:
<Grid myProj:MyBehavior.TheEvent="{x:Static InputElement.PointerPressedEvent}"/>
字符串格式
WinUI不允许在其绑定中使用string格式,而它是一项非常有用的功能,可以简化XAML代码。
<TextBlock Text="{Binding Path=Value, StringFormat='Value={0}'}"/>
如果说,绑定值等于5,则TextBlock将显示:
Value=5
它将在不需要绑定转换器或多个TextBlocks。
使用绑定设置设置器值
Avalonia和WPF允许将Style Setter值绑定到可视对象和非可视对象上的各种属性。例如:
<Style x:Key="MyStyle">
<Setter Property="Background"
Value="{Binding Path=MyBrushDefinedInViewModel}"/>
</Style>
WinUI不允许。
为什么它很重要——因为它提供了一种自定义控件的好方法。例如,如果希望样式中定义的某个控件模板可根据某个值进行自定义——在样式上创建相应的依赖项或附加属性,在ControlTemplate中绑定该属性,并将Style属性绑定到所需的变量。不幸的是,在WinUI中无法做到这一点,您必须使用控件模板复制和粘贴样式才能达到相同的效果。
生成的XAML代码在Avalonia(和WPF)中的可重用性比在基于WinUI的产品中要高得多。
XAML中的泛型类型
AFAIK 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"/>
最后,可以在XAML中将MyGenericClass类型的对象列表定义为:
<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依赖项属性的性能问题以及详细说明这些问题的链接。
https://www.codeproject.com/Articles/5366945/Multiplatform-XAML-Csharp-Miracle-Package-Avalonia
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)