Avalonia 学习笔记01. Images & Buttons(图片与按钮) (转载)
转载链接:Avalonia 学习笔记01. Images & Buttons(图片与按钮) - simonoct - 博客园
我对软件的图形化界面很感兴趣,查看了很多框架我最后选定C#的Avalonia作为我第一个学习的框架,不过我发现这个UI框架教程貌似很缺乏。
后面到YouTube上搜,看到一个作者制作了一系列教程,虽然是英文,但是我通过语音转文字+AI翻译学习,感觉还是挺不错的。
视频地址:https://youtu.be/ort9IqKAnL4?si=uTEjZ88osw1BLcyk
资源下载地址:https://github.com/angelsix/youtube/tree/develop/Avalonia BatchProcess
1.1 App.axaml
<Application xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" x:Class="AvaloniaApplication2.App" RequestedThemeVariant="Default"> <!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. --> <Application.Styles> <FluentTheme /> <StyleInclude Source="Styles/AppDefaultStyles.axaml"></StyleInclude> </Application.Styles> <Application.Resources> <SolidColorBrush x:Key="PrimaryForeground">#CFCFCF</SolidColorBrush> <SolidColorBrush x:Key="PrimaryBackground">#14172D</SolidColorBrush> <LinearGradientBrush x:Key="PrimaryBackgroundGradient" StartPoint="0%, 0%" EndPoint="100%, 0%"> <GradientStop Offset="0" Color="#111214"></GradientStop> <GradientStop Offset="1" Color="#151E3E"></GradientStop> </LinearGradientBrush> <SolidColorBrush x:Key="PrimaryHoverBackground">#333B5A</SolidColorBrush> <SolidColorBrush x:Key="PrimaryHoverForeground">White</SolidColorBrush> </Application.Resources> </Application>
-
<Application ...>: 这是根标签,代表你的整个应用程序。
xmlns="...": 这些是“命名空间”,可以理解为代码的“地址簿”。它告诉编译器<Button>、<Grid>这些标签应该去哪里找它们的定义。初学时不用太关心,知道是必须的就行。RequestedThemeVariant="Default": 设置应用的主题。Default 会跟随你的操作系统是亮色模式还是暗色模式。你也可以强制设为 Dark 或 Light。
-
<Application.Styles>: 应用的“样式规则手册”。这里面定义了整个应用的外观规则。<FluentTheme />: 引入 Avalonia 官方提供的 Fluent UI 主题。它给了所有控件(按钮、文本框等)一个现代化的、符合 Windows 11 风格的默认外观。<StyleInclude Source="Styles/AppDefaultStyles.axaml" />: 包含我们自己的样式文件。这行代码告诉“大管家”:“除了官方的 Fluent 主题,再把我们自己写的那个 AppDefaultStyles.axaml 文件里的样式规则也加载进来。” Source 就是文件的路径。使用自定义样式文件可以统一覆盖控件的默认样式,例如直接使用<Button>创建的按钮颜色、鼠标悬停颜色等等,如果一个个button指定颜色不仅混乱,如果遇到修改主题也会变得血雨腥风。
-
<Application.Resources>: 应用的“公共资源仓库”。这里存放着可以在整个应用中反复使用的“材料”,比如颜色、画刷、图标等。这样做的好处是,如果要改一个颜色,只需要改这里一处,所有用到它的地方都会自动更新。例如AppDefaultStyles.axaml里面就引用了默认的背景颜色、前景颜色,想要变更直接修改颜色即可。 -
<SolidColorBrush x:Key="PrimaryForeground">#CFCFCF</SolidColorBrush>:SolidColorBrush: 定义一个纯色画刷。你可以把它想象成一支只能画一种颜色的油漆刷。x:Key="PrimaryForeground": 给这支油漆刷贴上一个标签(唯一的钥匙),名字叫 PrimaryForeground。之后我们就可以通过这个名字来使用它。- #CFCFCF: 这是颜色的十六进制代码。
-
<LinearGradientBrush x:Key="PrimaryBackgroundGradient" ...>: 定义一个线性渐变画刷。 StartPoint="0%, 0%": 渐变从左上角开始。EndPoint="100%, 0%": 渐变到右上角结束。所以这是一个从左到右的水平渐变。<GradientStop Offset="0" Color="..." />: 在渐变轴的起点 (0%) 放置这个颜色。<GradientStop Offset="1" Color="..." />: 在渐变轴的终点 (100%) 放置这个颜色。
假设StartPoint="0%, 0%",EndPoint="100%, 100%",那么渐变轴就是右下角方向了。 (0%, 0%) <-- 起点 (StartPoint) +-------------------------+ (100%, 0%) | \ | | \ <-- 渐变轴 (Gradient Axis) | \ | | \ | +-------------------------+ (0%, 100%) (100%, 100%) <-- 终点 (EndPoint)
1.2 Styles/AppDefaultStyles.axaml
新建一个名为Styles的文件夹,然后模板文件可以用Rider新建的时候选择Avalonia Styles,命名为AppDefaultStyles.axaml即可。
这个文件专门用来写我们自定义的样式规则,让应用看起来更个性化。
<Styles xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <Design.PreviewWith> <Border Padding="20" Background="{DynamicResource PrimaryBackgroundGradient}" Width="200"> <!-- Add Controls for Previewer Here --> <Button Foreground="White" Content="Hello World"></Button> </Border> </Design.PreviewWith> <!-- Add Styles Here --> <Style Selector="Button"> <Setter Property="FontSize" Value="20"></Setter> <Setter Property="Foreground" Value="{DynamicResource PrimaryForeground}"></Setter> <Setter Property="Background" Value="{DynamicResource PrimaryBackground}"></Setter> </Style> <Style Selector="Button:pointerover /template/ ContentPresenter"> <Setter Property="Foreground" Value="{DynamicResource PrimaryHoverForeground}"></Setter> <Setter Property="Background" Value="{DynamicResource PrimaryHoverBackground}"></Setter> </Style> </Styles>
-
<Styles ...>: 这是一个专门存放样式规则的容器。 -
<Design.PreviewWith>: “设计师预览区”。这部分代码非常特别,它只在 IDE 的预览窗口里显示,不会被编译到最终的应用里。它的作用是给你提供一个“画板”,让你在设计样式的时候能立刻看到效果,不然就只能靠重新编译或者脑子想象。 -
<Style Selector="Button">: 定义一条样式规则。Selector="Button": 这个是“选择器”,意思是“这条规则将应用到我项目里所有的<Button>控件上”。<Setter Property="FontSize" Value="20" />:Setter: “设置器”,表示要在这里设置一个属性。Property="FontSize": 要设置的属性是“字体大小”。Value="20": 把字体大小的值设为 20。
<Setter Property="Foreground" Value="{DynamicResource PrimaryForeground}" />: 把文字颜色(Foreground)设置为我们之前在 App.axaml 里定义的、名叫 PrimaryForeground 的那个颜色资源。{DynamicResource ...} 的意思就是“去公共仓库按名字找资源”。
-
<Style Selector="Button:pointerover /template/ ContentPresenter">: 定义一条更特殊的规则。Selector="Button:pointerover": 这个选择器更具体,它说:“这条规则只在鼠标指针悬停在按钮上时才生效”。:pointerover 就是“鼠标悬停”这个特殊状态。/template/ ContentPresenter: 这是个固定写法。它的意思是,为了可靠地改变按钮在悬停时的背景和前景,我们需要更深入地指定到按钮内部一个叫 ContentPresenter 的部件。- 里面的两个
<Setter>就定义了当鼠标悬停时,按钮的文字颜色和背景色应该变成我们定义的“Hover”颜色。
1.3 MainWindow.axaml
这是用户能看到的、实际的主窗口。它负责把各种控件(按钮、图片等)组合、摆放起来,形成最终的界面。
<Window xmlns="https://github.com/avaloniaui" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" d:DesignWidth="1024" d:DesignHeight="600" Width="1024" Height="600" x:Class="AvaloniaApplication2.MainWindow" Title="AvaloniaApplication2"> <Grid Background="{DynamicResource PrimaryBackground}" ColumnDefinitions="Auto, *"> <Border Padding="20" Background="{DynamicResource PrimaryBackgroundGradient}"> <Grid RowDefinitions="*, Auto"> <StackPanel Spacing="12"> <Image Source="{SvgImage /Assets/Images/logo.svg}" Width="200"></Image> <Button Content="Home" HorizontalAlignment="Stretch"></Button> <Button Content="Process" HorizontalAlignment="Stretch"></Button> <Button Content="Actions" HorizontalAlignment="Stretch"></Button> <Button Content="Macros" HorizontalAlignment="Stretch"></Button> <Button Content="Reporter" HorizontalAlignment="Stretch"></Button> <Button Content="History" HorizontalAlignment="Stretch"></Button> </StackPanel> <Button Grid.Row="1" Content="Setting"></Button> </Grid> </Border> </Grid> </Window>
<Window ...>: 窗口。这是所有内容的根容器。d:DesignWidth/Height: 仅在设计预览时的窗口宽高,方便你设计。Width/Height: 程序真正运行时的窗口宽高。Title: 显示在窗口左上角的标题。
<Grid ...>: 网格。这是布局控件,就像一个看不见的 Excel 表格。Background="{...}": 把窗口的背景设置为我们定义的 PrimaryBackground 颜色。ColumnDefinitions="Auto, *": 定义网格的列。这行代码把网格分成了两列:Auto: 第一列的宽度由其内部最宽的内容自动决定。*: 第二列将占据所有剩余的可用空间。这是创建“侧边栏 + 主内容区”布局的经典手法。
<Border ...>: 边框容器。它像一个可以设置背景、边框和内边距的盒子。这里它被放在了 Grid 的第一列(Auto 列),用来创建侧边栏。Padding="20": 在这个盒子的四边都留出 20 个单位的内部空白,让内容不会紧贴着边缘。Background="{...}": 把这个侧边栏的背景设置为我们定义的那个渐变色画刷。
<Grid RowDefinitions="*, Auto">: 在侧边栏内部又放了一个网格。这个网格用来做垂直布局。RowDefinitions="*, Auto": 把这个内部网格分成了两行:*: 第一行占据所有剩余的垂直空间。Auto: 第二行的高度由其内容(设置按钮)自动决定。- 效果:这会把“设置”按钮“钉”在侧边栏的底部,而上面的部分则会填满剩余空间。
<StackPanel Spacing="12">: 堆叠面板。它是一个简单的布局控件,会把里面的东西一个接一个地堆起来(默认是垂直方向)。Spacing="12": 在每个被堆起来的控件之间,增加 12 个单位的间距。类似于word的行间距。- 这个 StackPanel 被放在了内部 Grid 的第一行 (* 行)。
<Image ...>: 图片。Source="{SvgImage /Assets/Images/logo.svg}": 设置图片的来源。{SvgImage ...}是用来加载 SVG 格式矢量图的特殊语法,路径是相对于项目根目录的。注意在Rider里面编辑图片属性,将Bulid Action修改为:AvaloniaResource,不然在构建程序的时候这些资源是不会构建的。Width="200": 设置图片显示的宽度为 200。- 注意需要安装一个名为Svg.Controls.Skia.Avalonia的包,教程展示用的是过时版本。
<Button ...>: 按钮。Content="Home": 按钮上显示的文字。HorizontalAlignment="Stretch": 水平对齐方式。Stretch 意味着让按钮的宽度拉伸,以填满其父容器(StackPanel)分配给它的所有水平空间。
<Button Grid.Row="1" Content="Setting" />: 放在底部的设置按钮。Grid.Row="1": 这是一个附加属性。它是在告诉父级的<Grid RowDefinitions="*, Auto">:“请把我这个按钮放在你的第二行(索引从0开始,所以1是第二行)!”,这是实现Setting图标“钉”在底部的关键。- 父级的
<Grid RowDefinitions="*, Auto">:第一行 (Row="0")** 的高度是*。意思是:“请占据所有可用的、剩余的垂直空间”,第二行 (Row="1") 的高度是Auto。意思是:“我的高度刚刚好能包住我的内容就行”。Row="1"就是Auto部分,实现setting在侧边栏的底部。 - setting会在左侧的原因貌似有些复杂,根据文档所说:HorizontalAlignment和VerticalAlignment默认值都是Stretch,也就是拉伸。但是这里表现却是和显式指定
Left一致。一个可能的解释是:因为父 Grid 列是 Auto。Auto 列的宽度由子控件的“理想尺寸”决定。虽然按钮的 HorizontalAlignment 默认是 Stretch,但它必须先报告一个尺寸给 Auto 列。这个尺寸就是它包裹其内容(HorizontalContentAlignment 为 Left)所需的最小尺寸。因此,Grid 列和按钮本身都收缩了,Stretch 没有机会发挥作用。 - 显式指定
HorizontalAlignment="Stretch"时,默认保守策略会被覆盖,找一个最大且具体(非无限宽)的宽度,然后拉伸。这里因为<Image ... Width=200>,所以最大宽度就是200。如果想让图标也居中,则需要额外指定HorizontalContentAlignment="Center"
1.4 <Grid>
Grid 的核心在于先定义网格,再放置内容。
1.4.1 定义行和列
用 <Grid.ColumnDefinitions>和<Grid.RowDefinitions>来定义网格的结构。每一列或每一行的大小有三种定义方式:
- Auto (按需分配):列宽或行高由其内部最宽/最高的内容决定。它会“收缩/撑开”以正好包裹住内容。
<Grid ColumnDefinitions="Auto, *"> <Button Grid.Column="0">短内容</Button> <Button Grid.Column="1">这里是很长很长的内容</Button> </Grid>
|-- 由“短内容”决定 --|---------- 占据所有剩余空间 -----------| | [ 短内容 ] | [ 这里是很长很长的内容 ] | | (Auto) | (*) |
- *** (星号/比例分配):这是最强大的方式。它会贪婪地抢占所有“剩余”的空间**。如果多个列/行都是 *,它们会按比例瓜分。
*, *:两个都分 1 份,所以是 1:1,即对半分。*, 2*:一个分 1 份,一个分 2 份,总共 3 份,所以是 1:2 的比例。
假设宽度是900px
|----------- 300px (1份) -----------|----------------- 600px (2份) -----------------|
| | |
| (*) | (2*) |
- 固定值 (Fixed Value):直接指定一个固定的数字,单位是设备无关像素。
<Grid ColumnDefinitions="100, *"> <Button Grid.Column="0">固定100宽</Button> <Button Grid.Column="1">剩余所有</Button> </Grid>
|---- 100px ----|------------------ 占据所有剩余空间 ------------------| | [ 固定100宽 ] | [ 剩余所有 ] | | (Fixed) | (*) |
1.4.2 放置控件
使用 Grid.Row 和 Grid.Column 附加属性,告诉每个控件应该去哪个“房间”(单元格)。索引从 0 开始。
<!-- 一个 2x2 的网格 --> <Grid ColumnDefinitions="*,*" RowDefinitions="*,*"> <!-- 左上角 (0,0) --> <Button Grid.Row="0" Grid.Column="0" Content="A"/> <!-- 右上角 (0,1) --> <Button Grid.Row="0" Grid.Column="1" Content="B"/> <!-- 左下角 (1,0) --> <Button Grid.Row="1" Grid.Column="0" Content="C"/> <!-- 右下角 (1,1) --> <Button Grid.Row="1" Grid.Column="1" Content="D"/> </Grid>
Column 0 (*) Column 1 (*) +-----------------+-----------------+ | | | Row 0 (*) | [ A ] | [ B ] | | (0,0) | (0,1) | | | | +-----------------+-----------------+ | | | Row 1 (*) | [ C ] | [ D ] | | (1,0) | (1,1) | | | | +-----------------+-----------------+
1.4.3 Grid 可以包含StackPanel吗?
正如上方教程展示的代码,Grid可以包含StackPanel,后续视频教程可能会深入这里我就简单找一例:
<Grid ColumnDefinitions="Auto, *"> <!-- Grid的第0列: 放置一个StackPanel来管理一堆按钮 --> <StackPanel Grid.Column="0" Margin="10" Spacing="8"> <Button>首页</Button> <Button>产品</Button> <Button>设置</Button> <Button>关于</Button> </StackPanel> <!-- Grid的第1列: 放置主内容 --> <Border Grid.Column="1" Background="LightGray"> <TextBlock VerticalAlignment="Center" HorizontalAlignment="Center"> 主内容区域 </TextBlock> </Border> </Grid>
<---------------------------------- Grid (整体框架) -----------------------------------> +---------------------------------+---------------------------------------------------+ | <-- StackPanel (在第0列) ------> | | | | | | +-------------------------+ | | | | [ 首页 ] | | | | +-------------------------+ | | | | [ 产品 ] | | | | +-------------------------+ | 主内容区域 | | | [ 设置 ] | | (第1列) | | +-------------------------+ | | | | [ 关于 ] | | | | +-------------------------+ | | | | | +---------------------------------+---------------------------------------------------+ | <------- Col 0 (Auto) --------> | <-------------------- Col 1 (*) -----------------> |
1.5 <StackPanel>
StackPanel 的核心是按顺序线性排列。它只有两个关键属性你需要关心。
1.5.1 Orientation (方向)
- Vertical (默认值): 从上到下堆叠。
- Horizontal: 从左到右排列。
<!-- 垂直方向 (默认) --> <StackPanel> <Button>A</Button> <Button>B</Button> <Button>C</Button> </StackPanel>
+-----------+ | [ A ] | +-----------+ | [ B ] | +-----------+ | [ C ] | +-----------+
<!-- 水平方向 --> <StackPanel Orientation="Horizontal"> <Button>A</Button> <Button>B</Button> <Button>C</Button> </StackPanel>
+---------+---------+---------+ | [ A ] | [ B ] | [ C ] | +---------+---------+---------+
1.5.2 Spacing (间距)
在每个子控件之间添加固定的空白。
<StackPanel Spacing="10"> <Button>A</Button> <Button>B</Button> </StackPanel>
+-----------+
| [ A ] |
+-----------+
|-- 10px空隙--| <-- Spacing
+-----------+
| [ B ] |
+-----------+
1.5.3 StackPanel 可以包含 Grid 吗?
可以
可能的场景:当你需要构建一个列表,而列表中的每一项本身又是一个复杂的布局时。例如,一个显示用户信息的垂直列表。
<StackPanel Spacing="10"> <!-- 第一张用户卡片 (一个 Grid) --> <Grid ColumnDefinitions="Auto, *"> <Image Grid.Column="0" Source="avatar1.png" Width="50"/> <StackPanel Grid.Column="1" Margin="10,0,0,0"> <TextBlock>张三</TextBlock> <TextBlock>软件工程师</TextBlock> </StackPanel> </Grid> <!-- 第二张用户卡片 (另一个 Grid) --> <Grid ColumnDefinitions="Auto, *"> <Image Grid.Column="0" Source="avatar2.png" Width="50"/> <StackPanel Grid.Column="1" Margin="10,0,0,0"> <TextBlock>李四</TextBlock> <TextBlock>UI 设计师</TextBlock> </StackPanel> </Grid> </StackPanel>
+---------------------------------------------+ <-- StackPanel 开始 | | | +-----------------------------------------+ | <-- 第一个 Grid 开始 | | | | | | | [头像1] | 张三 | | | | (Auto) | 软件工程师 | | | | | (*) | | | +-----------------------------------------+ | <-- 第一个 Grid 结束 | | |----------------- 10px 间距 -----------------| | | | +-----------------------------------------+ | <-- 第二个 Grid 开始 | | | | | | | [头像2] | 李四 | | | | (Auto) | UI 设计师 | | | | | (*) | | | +-----------------------------------------+ | <-- 第二个 Grid 结束 | | +---------------------------------------------+ <-- StackPanel 结束

浙公网安备 33010602011771号