目录
介绍
为什么Avalonia会大受欢迎
Avalonia的一些缺点
Web和Xamarin框架在多平台开发中的缺点
你可以在这篇文章中找到什么
如何阅读这篇文章
使用Visual Studio 2019创建和运行简单的Avalon项目
一些Avalonia内置控件
Avalonia控件简介
内置控件项目
TextBlock
TextBox
Button
ListBox
ComboBox
ToggleButton
CheckBox
ContextMenu
Menu
Popup
Window
Modal Window
ToolTip
TabControl
Avalonia 基元
介绍
基元示例代码位置
基元样本
Border
Viewbox
Image
Shapes
Avalonia 面板
介绍
Panels 示例
StackPanel
WrapPanel
Grid
DockPanel
Canvas
RelativePanel
Avalonia Brushes
介绍
Brushes
SolidColorBrush
LinearGradientBrush
RadialGradientBrush
ConicGradientBrush
Avalonia UI转换
RenderTransform与LayoutTransform
TranslateTransform
ScaleTransform
SkewTransform
更多关于转换的主题
结论
在Microsoft停止使用Silverlight后,唯一可用于所有主要平台的UI编程的多平台解决方案是Web解决方案 (JavaScript/TypeScript)和Xamarin。两者都是不足的,并且代表了从WPF富UI客户端解决方案的巨大倒退,如下面将详细解释的那样。
然而,有一个相对较新的开源.NET解决方案,Avalonia与WPF非常相似,在许多方面都比WPF更强大,并且(与WPF或UWP不同)适用于任何平台——Windows、Linux和MAC(我自己在每个平台上都对其进行了测试)那些平台)。Avalonia也适用于移动平台——iOS和Android(我还没有在移动平台上测试过Avalonia,所以我依赖Avalonia文档)。
所有这些范例都已在Avalonia中以至少与WPF中一样强大的方式实现。
一旦我们谈到优势,我们还应该提到Avalonia的一些镜头——请注意,由于Avalonia是一个快速发展的项目,它们中的大多数将很快得到修复。
与WPF/Avalonia编程相比,Web(JavaScript/TypeScript)开发有以下缺点:
现在关于Xamarin及其继任者MAUI的缺点。
本文及其计划后续文章的目的是填补不熟悉WPF或Silverlight的人在编程Avalonia UI时可能存在的空白。更复杂的问题将在以后的文章中描述。
本文中的所有样本都经过了以下测试:
本文仅涵盖Avalonia UI的基础知识——将成为任何应用程序一部分的构建块:
下一部分(第2部分)将涵盖以下主题:
之后,我还计划涵盖:
之后的文章将涵盖更复杂的主题,例如:
本文旨在成为Avalonia的动手教程,同时重点介绍Avalonia的基本功能。它充满了我建议在您的计算机上构建和运行的编码示例。如果您想很好地学习Avalonia功能,您还应该尝试创建自己的项目和示例,类似于本文中给出的那些。
阅读本文和复习练习不需要以前的WPF背景知识。
本文中的所有示例都是使用Visual Studio 2019、.NET 5.0和相应的C# 9.0创建的,尽管它们可以轻松降级到以前版本的.NET和C#。
首先,为了使用Avalonia,您需要使用位于“工具”下的VS扩展管理器或(对于VS2019)“扩展”菜单下的VS扩展管理器安装“Avalonia for Visual Studio”扩展。在网上找到这个扩展并安装它。该扩展包含各种Avalonia相关Visual Studio项目的模板、Avalonia特定文件类型和Avalonia XAML文件的智能感知(与WPF XAML文件略有不同)。
安装“Avalonia for Visual Studio”扩展后,启动Visual Studio并选择“Avalonia Application”项目类型:
按“下一步”按钮,选择项目的位置和项目名称,然后按“创建”按钮:
创建的项目将依赖于三个Avalonia包——Avalonia、Avalonia.Desktop和Avalonia.Diagnostics:
还创建了五个包含代码的文件——App.axaml、App.axaml.cs、MainWindow.axaml、MainWindow.axaml.cs和Program.cs。
带有“.axaml”扩展名的文件是重命名为“.axaml”的XAML文件,显然是为了将它们与WPF/UWP“.xaml”文件区分开来。Avalonia XAML语法与WPF XAML语法非常相似,在所谓的样式选择器方面有一些特殊性(将在以后的文章中解释)。
在上述五个文件中,您可能需要修改MainWindow.axaml和MainWindow.axaml.cs文件最多,然后可能稍微更改App.axaml和App.axaml.cs文件,您可能不必更改Program.cs文件。
您可以按原样运行空窗口,但在MainWindow中放置一些代码会更有趣。
打开MainWindow.xaml文件并将其内容(默认由"Welcome to Avalonia!"文本组成)替换为以下代码:
如果您现在运行应用程序,窗口中间会有一个Button(由水平和垂直对齐确保——单词“关闭窗口”将写在按钮中间(Button的内容)和从“关闭按钮”文本到按钮两侧的边距将在左右为10个通用像素,在顶部和底部为5个通用像素(由Padding属性指定):
到目前为止一切顺利,但如果你按下按钮,什么都不会发生。我们需要尝试将按钮单击事件连接到关闭窗口的操作。在第一个示例中,我们将使用最简单但也是最糟糕的方式来实现此目的 ——后置代码。文件MainWindow.xaml.cs包含所谓的“代码隐藏”——MainWindow.xaml文件的C#代码:
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
#if DEBUG
this.AttachDevTools();
#endif
}
private void InitializeComponent()
{
AvaloniaXamlLoader.Load(this);
}
}
我们将按钮命名为“ CloseWindowButton”。在WPF中,会生成相应的类成员。Avalonia,仍然不包含这个功能,但是我们可以通过添加以下行轻松找到该按钮:
var button = this.FindControl
当InitializeComponent();在构造函数中被调用之后。
然后我们可以为按钮的Click事件添加一个处理程序button.Click += Button_Click;:
最后,在处理程序中,我们可以调用窗口上的Close()方法:
private void Button_Click(object? sender, RoutedEventArgs e)
{
this.Close();
}
您必须在文件顶部有一个using Avalonia.Interactivity;声明才能引用RoutedEventArgs。
MainWindow构造函数和处理程序的完整代码如下所示:
public MainWindow()
{
InitializeComponent();
#if DEBUG
this.AttachDevTools();
#endif
var button = this.FindControl
现在,如果您运行应用程序并按“关闭”按钮,窗口将关闭。
此应用程序的代码位于NP.Demos.SimpleAvaloniaProject.sln下,但如果您是Avalonia新手,则必须复习上面的练习。
在本节中,我将描述一些对构建应用程序最有帮助的内置Avalonia控件。请注意,为简洁起见,我不会描述每个内置控件,只描述最常用的控件。
如果您想了解其余的内置控件,您可以:
包含大部分(如果不是全部)内置控件的Windows应用程序将Popup,您将能够看到各种控件的功能。通过在Samples/Pages/ Visual Studio文件夹下的XAML代码中跟踪它们,您还可以了解如何创建和更改这些控件的属性。
新控件的创建和自定义将在以后的文章中讨论,以及最有用的控件ContentPresenter,ItemsPresenter它们可以相应地为非可视对象或非可视对象集合提供可视表示。
本节的目的是概述内置Avalonia控件,而不是对其功能的详细描述。
请注意,Avalonia中的Control类比WPF中的同名类更原始——Avalonia Control没有模板。具有模板的控件继承自实现了ITemplatedControl接口的TemplatedControl类。因为那个Avalonia Image、Shape和Panel类是从Control派生的,而不是从Visual类派生的(因为它们在WPF中)。
未来文章中将提供哪些模板的说明。
在本小节中,我们将描述WPF意义上的一些最有用的控件,即模板化控件,而不是更原始的图像、形状和面板。
这些小节的代码位于NP.Demos.BuiltInControls.sln解决方案下。
如果您构建和运行解决方案,您将看到以下内容:
在这里,我描述了一些我认为最有用的内置控件。特定于这些控件的所有代码都位于MainWindow.xaml文件中。让我们回顾一下每个控件并描述与它们相关的XAML代码。
TextBlock是本节中描述的控件中唯一不能重新模板化的控件——它派生自Control(而不是TemplatedControl类)。所以它更像是一个原始的,而不是WPF意义上的复合控件,但我把它放在这里,因为它是最重要的构建块之一,并且(从我的角度来看)应该首先描述。
TextBlock表示简单的文本。它最重要的属性是C# string类型的Text。Text包含要显示为TextBlock的文本。
TextBlock有很多允许文本自定义的属性,包括:
上面列出的许多属性也适用于可能具有文本的其他控件,例如按钮、菜单、列表框等。
创建文本块的简单XAML代码如下:
这是TextBox示例的图像:
如果您在TextBox中键入任何内容,则其属性Text将使用键入的文本进行更新。
我使用绑定来复制在TextBox中键入的文本,下面放置了TextBlock。这是XAML代码:
我们用两行Grid定义了面板——文本框位于顶行(Grid.Row="0")和TextBlock第二行(Grid.Row="1")。
我们使用ElementName绑定: Text="{Binding Path=Text, ElementName=TheTextBox}"将TextBlock的Text属性绑定到TextBox的Text属性(通过其x:Name属性命名为“TheTextBox”)。这会产生下面的文本重复输入到TextBox。
我们将描述的下一个控件是Button:
这是我们按钮的代码:
Padding="10,5"以上表示按钮从其内容向左和向右延伸 10个通用像素以及在顶部和底部延伸5个像素。Grid.Row="1"表示按钮位于第二行Grid(第一行被表头占据)。
按钮定义Click路由事件——单击按钮时触发。有三种方法可以在单击按钮时调用C#代码:
ListBox显示能够一次选择一个项目的项目集合。如果项目数超过ListBox的大小,它将显示滚动条。
使用列表框的最佳方法是将其Items属性绑定到集合。如何做到这一点将在下面显示。在我们的例子中,我们只是在XAML代码中创建了ListBoxItem来填充它:
ListBox最重要的属性是上面提到的和选择相关的Items属性:SelectedIndex和SelectedItem。这个ListBox的SelectedIndex绑定到下一小节中描述的ComboBox的SelectedIndex,因此当您更改它们中的每一个上的选定项目时,另一个将以相同的方式做出反应。
ComboBox在各种其他框架中也被叫做DropDownBox。就像ListBox还存储项目集合一样,但始终只显示选定的项目——其他项目仅在鼠标指针单击其右侧的箭头时显示在Popup窗口(或更确切地说是下拉菜单)中。上图显示ComboBox没有选定项目但打开的dropdown。这是我用来创建和填充的ComboBox代码:
与ListBox的情况相同,ComboBox的主要属性是Items(绑定到集合)SelectedIndex和SelectedItem。该示例显示了如何将ComboBox的SelectedIndex绑定到ListBox它左侧的: SelectedIndex="{Binding Path=SelectedIndex, ElementName=TheListBox, Mode=TwoWay}",这样当一个更改选择时,另一个也将更改。
ToggleButton是一个具有两种状态的控件——Checked和Unchecked由其布尔属性IsChecked控制。每次单击按钮时,其IsChecked属性都会将其值从false到true切换,反之亦然。按钮的背景会根据它是否被选中而改变。
同样,为了展示绑定的强大功能,我将ToggleButton的IsChecked属性绑定CheckBox到它旁边的属性,以便它们同步更改。
CheckBox与ToggleButton非常相似,但看起来不同(如您所见)。这是CheckBox的XAML代码:
您可以看到将其IsChecked属性连接到左侧ToggleButton属性的绑定。
CheckBox还有一个switch属性IsThreeState,当它设置为true时,CheckBox可以在三种状态之间切换——false、true和undefined——对应于它的IsChecked属性设置为null:
这是三种状态的CheckBox代码:
在某个区域或控件上单击鼠标右键时打开ContextMenu。这是代码:
菜单通常放在窗口的顶部,但也可以出现在其他地方。以下是Menu示例的XAML代码:
Popup是一个控件,它在所谓的Popup的PlacementTarget旁边打开一个轻量级窗口:
Popup窗口是否打开由其IsOpen属性控制(和反映),在我们的例子中,该IsChecked属性与ToggleButton控制(和反映)Popup窗口状态的属性相关联:
我们使用两种方式绑定来将Popup的IsOpen属性绑定到ToggleButton的IsChecked属性,以便更改它们中的每一个都会影响另一个。
StaysOpen属性设置为false——意味着单击Popup区域将关闭Popup窗口。
PlacementTarget属性指定Popup要定位的元素。
PlacementMode="Bottom"表示Popup将定位在放置目标的底部。
当您单击“Window”示例的按钮时,将打开一个小窗口。
下面是窗口示例的XAML代码:
打开窗口的C#代码是通过代码隐藏挂钩的——不是因为它是一种很好的方法(实际上它是上面提到的最糟糕的方法)——而是因为它是最简单的方法和最容易理解的方法。以下是MainWindow.asaml.cs文件中的相关C#代码:
public MainWindow()
{
...
var openWindowButton = this.FindControl
在MainWindow的构造函数中(在调用InitializeComponent()之后),我们通过按钮的名称找到按钮并将处理程序附加到它的Click事件。在该处理程序中,我们创建新Window对象并在其上调用Show()方法以显示(打开)窗口。
模态窗口也称为对话框——它是一个在关闭之前阻止对其祖先窗口进行任何操作的窗口。
按“打开模态(对话)窗口”按钮将打开一个对话窗口,该窗口完全阻塞主窗口,直到它被关闭。这是XAML代码:
同样,我们使用不好的代码隐藏范例连接C#代码:
public MainWindow()
{
...
var openModalWindowButton = this.FindControl
此示例与之前示例之间的唯一区别是,这里我们调用sampleWindow.ShowDialog(...)方法而不是sampleWindow.Show()方法,将当前窗口作为对话框的父级传递给它。
ToolTip是在定义该ToolTip元素的鼠标指针旁边打开的临时Popup窗口:
我们在XAML中定义的ToolTip如下:
大多数时候ToolTip只是一个文本(就像我们的例子一样),但有时它会变得更加复杂,这将在未来进行描述。
TabControl允许显示不同的选项卡——每个选项卡包含不同的内容:
在我们示例中的选项卡之间切换会将显示的文本从“Hello World!”更改为“Hi World!”。
这是实现这一目标的非常简单的XAML代码:
基元是不可组合的Avalonia UI控件(它们也不是面板),它们源自Control而不是源自TemplatedControl。在WPF中,它们甚至不会被称为控件,而是视觉对象。上面已经描述了其中一个基元——它是TextBlock元素。
除了上面介绍的以外,TextBlock最重要的基元是Border、Viewbox、Image和Shape。形状是派生自Shape的控件——其中最常用的是Path(对于任何形状)Line、Rectangle和Ellipse。
本节的代码位于NP.Demos.Primitives解决方案下:
我将为每个基元类型使用TabControl的一个选项卡,而不是像上一节那样为每个基元类型使用一个页面。这样,我们可以更详细地了解这些非常重要的Avalonia基本构建块。
Border示例的选项卡容器显示在上图中。这是用于创建带有内部文本的此类边框的XAML代码:
TextBlock “边框示例”放置在Border中——它将Border.Child属性设置为TextBlock。
重要提示:我建议不要设置边框Child属性,而是将边框放置在Grid面板内,并将您希望位于边框内的元素放置在与边框兄弟相同的面板内。这是因为我的WPF经验是,如果边框有阴影(并且通常需要),它们的后代元素会稍微抖动。但是,不是边框的直接后代的元素不会受到影响。我还没有测试过它在Avalonia中是如何工作的——这个建议是基于我的WPF经验。
Viewbox是一个控件,允许在视觉上缩小或扩展其中的所有内容。就像边框一样,它具有放置内容的Child属性。您可以在其中放置任何复杂的视觉元素或包含许多视觉元素的面板,并且所有内容都将根据Viewbox参数在视觉上调整大小。
Viewbox将从其后代那里获得大小。Viewbox的子控件总是会缩小到最小尺寸,即使它的HorizontalAlignment和VerticalAlignment属性设置为Stretch,它自己的后代也允许。如果Viewbox的子控件没有指定大小并且不能从它自己的子控件那里得到大小,例如,一个Grid没有Height和Width没有任何可以定义其大小的子控件,它将缩小到大小为0。
我们在“Viewbox”选项卡下的例子展示了当Viewbox的Stretch属性设置为不同值时,标签TextBox是如何变化的。您可以通过将左侧的Stretch属性设置为不同的值然后尝试调整窗口大小来自己玩它。
这是我们Viewbox示例的代码:
这是我们缩小控件时的样子,并且Stretch="None"。
如果将拉伸设置为None——视图框的子级根本不会调整大小——因此当您缩小窗口——您将切入如上所示的viewbox内容。
这是图片的Stretch="Fill":
Viewbox的子视图的width和height根据width和size的变化比例调整——在我们的例子中,我们使width变小(变薄),而使高度变大。在"Fill"下,不保留原始纵横比(width/height),但一切都取决于Viewbox调整大小的方式。
其余可能的Stretch值确实保留了原始控件的纵横比。以下是当我们在Stretch="Uniform"的情况缩小width但增加height时会发生:
控件在保留原始纵横比的同时收缩(或扩展),以便它们都可以适应空间。
最后,当Stretch="UniformToFill"——子控件仍然保留纵横比时,它的一个维度完全填充给它的空间,而另一个维度被切割——如下图所示——Y维度适合height,而X维度被切割:
请注意,其他一些基元——Images和Shapes也具有以完全相同的方式表现的Stretch属性。
您应该使用Image选项卡下的Image示例:
尝试选择不同Stretch的模式,看看每种模式下的调整Image大小有何不同。
以下是Image示例的相关XAML代码:
Image上定义的最重要的属性是Source。在XAML中,它指向实际的Image png或jpg或其他文件:Source="/Images/LinuxIcon.jpg"。请注意,图像文件LinuxIcon.jpg是在同一个项目中定义的,它的Build Action是AvaloniaResource:
请注意,该Image类的Source属性是类型IImage,因此如果您想在C#代码中分配它,您必须编写,例如:
Image image = this.FindControl("TheImage");
var assets = AvaloniaLocator.Current.GetService();
image.Source = new Bitmap(assets.Open
(new Uri("avares://NP.Demos.Primitives/Images/LinuxIcon.jpg")));
XAML类型转换使Source赋值变得相当简单,但有时,您无法避免使用C#。
请注意,Bitmap是IImage的实现之一,这就是上面的代码有效的原因。
URL“avares://NP.Demos.Primitives/Images/LinuxIcon.jpg”中的神秘前缀“avares”代表“Avalonia Resource”,而不是埃及希克索斯的首都。
形状是——各种几何形状。以下是“形状”选项卡的外观:
您可以使用Stretch,StrokeThickness和Fill属性。您可以看到Rectangle和Ellipse不受Stretch影响,这是有道理的,因为它们是由它们的width和height决定的。
Line和Path受到Stretch的影响,就像Image和Viewbox一样。
StrokeThickness决定边框的粗细。Stroke属性(类型IBrush)确定边框的颜色。
Fill指定内部的颜色——在我们的例子中,当HasFill checkbox打开时,Fill是red,当它不打开时,Fill是null(本质上是透明的,但也命中测试不可见)。
这是该Line的XAML代码:
主要的line定义属性是确定line的起点StartPoint和EndPoint终点。
Rectangle和Ellipse两者的形状由它们的width-height决定——它们应该明确指定,或者由它们的容器为它们提供多少空间。
Path是最通用的形状。它由类型Geometry的Data属性决定。为不同的形状创建几何图形本身就是一门科学,超出了本教程的范围。对于现成的几何图形,您可以转到Material Design Icons,选择您想要的图标并检查其XAML表示。
这是我们Path示例的代码:
Data设置为从Material Design Icons复制的一些神秘的Geometry string。
面板是Avalonia原始控件,用于安排放置在其中的其他控件。除了它们的Background color之外,面板本身没有任何视觉表示,但它们对于排序和安排其他控件是必不可少的。
面板代码位于NP.Demos.Panels.sln解决方案下。
当您打开应用程序时,您将加载StackPanel选项卡:
三个方形按钮以100x100通用像素排列成一个堆栈——左侧是垂直堆栈,右侧是水平堆栈。
这是垂直堆栈的XAML代码:
Orientation="Vertical"是定义什么方向的。垂直方向是默认设置——因此可以跳过第一个属性。
为了实现右侧显示的水平方向,只需将Orientation替换为Orentation="Horizontal"。这样就实现了右侧的堆叠。
如果您调整窗口大小,您会看到当窗口变得太小时,窗口的StackPanels末端会被剪掉。WrapPanel解决了这个问题。
切换到“WrapPanel”选项卡,以便使用WrapPanel。如果你把它变小,你会看到最后一个项目而不是切割——包装,垂直面板将最后一个项目包装到右边,水平——到底部:
Net选项卡显示Grid,其是最复杂和最有用的面板:
我们的Grid有4行和4列——这是它们的定义方式:
由于用于键入行和列定义的Avalonia快捷方式,上述定义是允许的。通常的WPF方式也是允许的,这里的定义看起来如何:
您可以看到Avalonia快捷方式节省了大量空间。
这是行高和列宽的解释。第一行的(Height="80")高度为80个通用像素。第二行(Height="Auto")根据其内容调整大小——即,其大小取决于其包含的内容。第三和第四行是星形行(Height="*"和Height=2*)。所有星形行和列一起占据了Grid允许占据的所有剩余空间,并且它们之间的空间基于它们的星形系数进行分配。由于最后一行的系数为2,因此分配给它的空间将始终是分配给最后一行的空间的两倍。
列的宽度以完全相同的方式计算。
打开应用程序的“Grid”选项卡,并通过水平和垂直调整窗口大小来进行操作。
DockPanel允许将其子项安排在其两侧,而最后一个子项(未停靠)将占据其余空间。
停靠值由附加属性确定,该DockPanel.Dock属性可以采用值Left、Top、Right和Bottom。
在我们的示例中,我们有8个按顺时针方向排列的按钮——Left、Top、Right,Bottom然后是Left、Top、Right,Bottom最后一个按钮占据了其余空间:
这是我们DockPanel示例的代码:
请注意,垂直停靠按钮的width和水平停靠按钮的height是30。
Canvas是一个面板,它允许通过左上角的坐标来放置控件,由附加属性Canvas.Left和Canvas.Top决定::
在上面的canvas上,按钮被放置在距离左上角右侧300像素和底部200像素的位置。这是XAML代码:
WPF中不存在RelativePanel。但是,它可能非常有用,尤其是在为平板电脑和手机编码时。它允许相对于面板和相对于同一面板内的其他命名元素指定元素的位置。
这是我们的RelativePanel示例的外观:
RelativePanel提供了许多所谓的附加属性,允许选择它的子项相对于面板本身或相对于同一面板的其他命名子项的位置。以下是上述示例的XAML代码:
你可以从这个例子中看到,你可以通过使用RelativePanel.AlignBottomWithPanel和RelativePanel.AlignRightWithPanel属性将控件对齐到任何RelativePanel侧。还可以使用属性RelativePanel.RightOf和RelativePanel.Below将控件放置到另一个控件的右下方。
就像WPF一样,Avalonia具有:
除了WPF Brushes之外,Avalonia Brushes也有ConicGradientBrush等级。
该Brushes示例位于NP.Demos.Brushes.sln解决方案下:
SolidColorBrush被前两个示例覆盖:第一个通过名称(Background="Red")指定颜色,第二个——通过ARGB值(Background="#FF43A047")。
这是LinearGradientBrush示例的XAML:
起点和终点的坐标从控件的左上角开始计算(在我们的例子中是Button)。在我们的例子中——StartPoint是左上角,终点是右下角(100%,100%点)。
渐变色标指定沿线段从起点到终点的比例偏移量以及应位于相应偏移量处的颜色。
这是RadialGradientBrush的XAML代码:
RadialGradientBrush有两个重点——GradientOrigin和Center。渐变圆排列在连接这两点的直线上,圆心在偏移量0处收敛于GradientOrigin,在偏移量1处收敛于Center。圆的大小由Radius(0和1之间的双精度数)控制。
Avalonia RadialGradientBrush不如WPF强大,因为WPF允许输入两个数字作为半径:RadiusX和RadiusY允许在圆的顶部使用椭圆。
这是ConicGradientBrush的XAML代码:
在ConicRadialBrush的例子中,颜色围绕Center属性指定的某个点呈锥形排列。Angle属性指定垂直轴和第一种颜色(偏移0处的颜色)之间的顺时针角度,在我们的例子中,它是90度,所以红色水平开始。
变换允许在任何Avalonia UI控件上进行2D线性仿射变换。
转换代码位于NP.Demos.Transforms.sln解决方案下。
在WPF中,每个元素都可以在其上运行渲染和布局转换。RenderTransform布局完成后执行变换(不影响控件周围的布局)。LayoutTransform在计算出新的(转换后的)控件的坐标后执行布局操作。
因此,RenderTransform比LayoutTransform更频繁地使用,Avalonia允许RenderTransform在每个Avalonia控件(与WPF相同)上执行,但LayoutTransform只能在LayoutTransformControl上执行。如果您需要对任何控件执行LayoutTransform,你可以让它成为LayoutTransformControl的子控件,并在LayoutTransformControl上执行LayoutTransform,如我们的示例所示。
如果您尝试运行示例并更改RotationTransform Rotation角度,您会得到以下结果:
您可以看到Render Transformed按钮将显示在包含它的Grid面板之外,而Layout Transformed按钮将展开其Grid容器(容器面板以浅蓝色显示)。
以下是示例的相关代码:
可以看到,为了实现布局转换,out 按钮被放置在LayoutTransformControl里面,并且RotateTransform应用到LayoutTransformControl而不是按钮本身。
RotateTransform的重要属性是以度为单位的角度,它指定旋转角度。
更改TranslateTransform的属性X和Y将控件向右移动X像素并将Y像素移动到底部:
缩放变换水平或垂直扩展或缩小控件。水平和垂直缩放分别由ScaleX和ScaleY属性控制:
SkewTransform根据图像的AngleX和AngleY属性水平或垂直(或两者)倾斜图像:
Avalonia具有MatrixTransform通用线性仿射变换。所有其他可用的Avalonia转换(旋转、平移、缩放和倾斜转换)都只是MatrixTransform的private情况。但是,它很少使用,因为它不直观。
可以通过将多个变换放在TransformGroup对象内来组合它们。
本文的目的是让没有WPF背景的人开始使用Avalonia进行多平台编码。本文专门介绍基本的Avalonia构建块。
我计划写更多关于其他令人兴奋的Avalonia主题的文章,包括但不限于绑定、MVVM模式、模板、样式、行为、安排代码以获得最佳编码等。
https://www.codeproject.com/Articles/5308645/Multiplatform-UI-Coding-with-AvaloniaUI-in-Easy-Sa#SimpleProject