跨平台WPF框架Avalonia教程 五
样式
Avalonia UI 的样式系统是一种可以在控件之间共享属性设置的机制。
提示
在 Avalonia 中,Style
更类似于 CSS 样式,而不是 WPF/UWP 样式。在 Avalonia 中,与 WPF/UWP 中的样式相当的是ControlTheme。
工作原理
实质上,样式机制有两个步骤:选择和替换。样式的 XAML 可以定义如何进行这两个步骤,但通常你会在控件元素上定义 'class' 标签来帮助选择步骤。
信息
Avalonia UI 样式系统使用在控件元素上的 'class' 标签与 CSS(层叠样式表)在 HTML 元素上的工作方式类似。
在选择步骤中,样式系统从控件开始沿着逻辑树向上搜索。这意味着在应用程序的最高级别(例如 App.axaml
文件)定义的样式可以在应用程序的任何地方使用,但仍然可以在控件更近的地方(例如在窗口或用户控件中)进行覆盖。
当选择步骤找到匹配项时,匹配的控件的属性将根据样式中的设置器进行更改。
如何编写
样式的 XAML 有两个部分:选择器属性和一个或多个设置器元素。选择器的值包含使用 Avalonia UI 样式选择器语法 的字符串。每个设置器元素通过名称标识将被更改的属性和将被替换的新值。模式如下:
<Style Selector="selector syntax">
<Setter Property="property name" Value="new value"/>
...
</Style>
信息
Avalonia UI 样式选择器语法 类似于 CSS(层叠样式表)中使用的语法。有关详细的参考信息,请参阅 此处。
示例
以下是样式如何编写并应用于控件元素的示例,使用样式类来辅助选择:
<Window ... >
<Window.Styles>
<Style Selector="TextBlock.h1">
<Setter Property="FontSize" Value="24"/>
<Setter Property="FontWeight" Value="Bold"/>
</Style>
</Window.Styles>
<StackPanel Margin="20">
<TextBlock Classes="h1">Heading 1</TextBlock>
</StackPanel>
</Window>
在此示例中,所有带有 h1
样式类的 TextBlock
元素将显示为样式设置的字体大小和字重。这在预览面板中工作:
放置样式的位置
样式放置在 Control
或 Application
上的 Styles
集合元素中。例如,窗口的样式集合如下:
<Window.Styles>
<Style> ... </Style>
</Window.Styles>
样式集合的位置定义了其中包含的样式的范围。在上面的示例中,样式将应用于窗口及其所有内容。如果样式添加到 Application
,则将全局应用。
选择器
样式选择器定义样式将作用于哪些控件。选择器使用多种格式,其中最简单的一个如下:
<Style Selector="TargetControlClass.styleClassName">
这个选择器将匹配具有样式键TargetControlClass
且带有样式类styleClassName
的所有控件。
信息
完整的选择器列表可在此处找到。
设置器
设置器描述了当选择器与控件匹配时会发生什么。它们是以以下格式编写的简单的属性/值对:
<Setter Property="FontSize" Value="24"/>
<Setter Property="Padding" Value="4 2 0 4"/>
当样式与控件匹配时,样式中的所有设置器都将应用于控件。
信息
有关设置器的更多信息,请参阅此处.
嵌套样式
样式可以嵌套在其他样式中。要嵌套样式,只需将子样式作为父 <Style>
元素的子元素包含,并在子选择器的开头加上 嵌套选择器 ^:
<Style Selector="TextBlock.h1">
<Setter Property="FontSize" Value="24"/>
<Setter Property="FontWeight" Value="Bold"/>
<Style Selector="^:pointerover">
<Setter Property="Foreground" Value="Red"/>
</Style>
</Style>
当发生这种情况时,父样式的选择器将自动应用于子样式。在上面的示例中,嵌套样式的选择器将是 TextBlock.h1:pointerover
,这意味着当指针悬停在控件上时,它将显示为红色前景色。
信息
嵌套选择器必须存在,并且必须出现在子选择器的开头。
样式键
样式选择器匹配的对象的类型不是由控件的具体类型决定的,而是通过检查其 StyleKey
属性来确定的。
默认情况下,StyleKey
属性返回当前实例的类型。然而,如果你希望你的控件(继承自 Button)被样式化为一个按钮,你可以在你的类中重写 StyleKeyOverride
属性,并让它返回 typeof(Button)
。
public class MyButton : Button
{
// MyButton 将会被作为标准的 Button 控件样式化。
protected override Type StyleKeyOverride => typeof(Button);
}
信息
请注意,这与 WPF/UWP 相比逻辑是相反的:在这些框架中,当你派生一个新的控件时,它将被样式化为其基础控件,除非你覆盖 DefaultStyleKey
属性。在 Avalonia 中,控件将使用其具体类型进行样式化,除非提供了不同的样式键。
信息
在 Avalonia 11 之前,样式键是通过实现 IStyleable
并提供新的 IStyleable.StyleKey
属性实现的。Avalonia 11 仍然支持这种机制以保持兼容性,但在未来的版本中可能会删除。
样式和资源
样式通常与资源一起使用以帮助维护一致的表现。资源可以帮助定义应用程序中的标准颜色和图标,或者在从单独文件中包含时可以跨多个应用程序中使用。
样式类(Style Classes)
你可以为 Avalonia UI 控件分配一个或多个样式类,并使用它们来指导样式选择。样式类通过在控件元素中使用 Classes
属性进行分配。如果你想分配多个类,则使用空格分隔它们。
例如,这个按钮同时应用了 h1
和 blue
样式类:
<Button Classes="h1 blue"/>
伪类(Pseudo Classes)
与 CSS 类似,控件可以拥有伪类,这些类是在控件本身而不是用户定义的。伪类在选择器中的名称始终以冒号开头。
例如,:pointerover
伪类表示指针输入当前悬停在控件上(在控件的边界内)。(这个伪类类似于 CSS 中的 :hover
。)
这是一个使用 :pointerover
伪类选择器的示例:
<StackPanel>
<StackPanel.Styles>
<Style Selector="Border:pointerover">
<Setter Property="Background" Value="Red"/>
</Style>
</StackPanel.Styles>
<Border>
<TextBlock>I will have red background when hovered.</TextBlock>
</Border>
</StackPanel>
在此示例中,伪类选择器更改了控件模板内的属性:
<StackPanel>
<StackPanel.Styles>
<Style Selector="Button:pressed /template/ ContentPresenter">
<Setter Property="TextBlock.Foreground" Value="Red"/>
</Style>
</StackPanel.Styles>
<Button>I will have red text when pressed.</Button>
</StackPanel>
其他伪类包括 :focus
、:disabled
、:pressed
(用于按钮)和 :checked
(用于复选框)。
信息
有关伪类的更多详细信息,请参阅参考 此处.
条件类(Conditional Classes)
如果你需要使用绑定条件添加或删除类,则可以使用以下特殊语法:
<Button Classes.accent="{Binding IsSpecial}" />
代码中的类(Classes in Code)
你可以使用 Classes
集合在代码中操作样式类:
control.Classes.Add("blue");
control.Classes.Remove("red");
控件主题(Control Themes)
控件主题是在样式的基础上构建的,用于为控件创建可切换的主题。尽管控件主题与 WPF/UWP 中的样式类似,但它们的机制略有不同。
提示
因为控件主题是基于样式的,所以首先需要理解 Avalonia 的样式系统。
简介
在 Avalonia 11 之前,控件主题是使用标准样式创建的。然而,这种方法存在一个根本性的问题:一旦样式被应用到控件上,就没有办法移除它。因此,如果你想为特定的控件实例或用户界面(UI)部分更改主题,唯一的选择是将第二个主题应用到控件上,并希望它能覆盖原始主题中设置的所有属性。
这个问题的解决方案在 Avalonia 11 中引入,形式为 控件主题。
控件主题本质上是样式,但有一些重要的区别:
- 控件主题没有选择器:它们有一个
TargetType
属性,用于描述它们要针对的控件。 - 控件主题存储在
ResourceDictionary
中,而不是Styles
集合中。 - 控件主题通过设置
Theme
属性来分配给控件,通常使用{StaticResource}
标记扩展。
信息
控件主题通常应用于模板化(无外观)的控件,但实际上它们可以应用于任何控件。然而,对于非模板化的控件,通常更方便使用标准样式。
示例:圆形按钮
以下示例显示了一个简单的 Button
主题,它显示一个带有椭圆背景的按钮,具有 90 年代的 Geocities 风格:
App.axaml
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="AvaloniaApplication.App">
<Application.Styles>
<FluentTheme />
</Application.Styles>
<Application.Resources>
<ControlTheme x:Key="EllipseButton" TargetType="Button">
<Setter Property="Background" Value="Blue"/>
<Setter Property="Foreground" Value="Yellow"/>
<Setter Property="Padding" Value="8"/>
<Setter Property="Template">
<ControlTemplate>
<Panel>
<Ellipse Fill="{TemplateBinding Background}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
<ContentPresenter x:Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"/>
</Panel>
</ControlTemplate>
</Setter>
</ControlTheme>
</Application.Resources>
</Application>
MainWindow.xaml
<Window xmlns="https://github.com/avaloniaui"
xmlns:x='http://schemas.microsoft.com/winfx/2006/xaml'
x:Class="Sandbox.MainWindow">
<Button Theme="{StaticResource EllipseButton}"
HorizontalAlignment="Center"
VerticalAlignment="Center">
Hello World!
</Button>
</Window>
控件主题中的交互
与标准样式一样,控件主题支持嵌套样式,可以用于添加指针悬停和按下状态等交互。
示例:圆形按钮悬停状态
使用嵌套样式,我们可以使按钮在指针悬停在其上时改变颜色:
App.axaml
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="AvaloniaApplication.App">
<Application.Styles>
<FluentTheme />
</Application.Styles>
<Application.Resources>
<ControlTheme x:Key="EllipseButton" TargetType="Button">
<Setter Property="Background" Value="Blue"/>
<Setter Property="Foreground" Value="Yellow"/>
<Setter Property="Padding" Value="8"/>
<Setter Property="Template">
<ControlTemplate>
<Panel>
<Ellipse Fill="{TemplateBinding Background}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
<ContentPresenter x:Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"/>
</Panel>
</ControlTemplate>
</Setter>
<Style Selector="^:pointerover">
<Setter Property="Background" Value="Red"/>
<Setter Property="Foreground" Value="White"/>
</Style>
</ControlTheme>
</Application.Resources>
</Application>
控件主题查找
控件主题有两种查找方式:
- 如果控件的
Theme
属性被设置,则使用该控件主题;否则 - Avalonia 会从逻辑树向上搜索控件,查找一个
x:Key
与控件的样式键匹配的ControlTheme
资源
提示
如果你在使用 Avalonia 时发现控件无法找到主题,请确保它返回了与控件主题的 x:Key
和 TargetType
匹配的样式键。
实际上,这意味着您有两种选择来定义控件主题:
- 如果您希望控件主题应用于控件的所有实例,请将
{x:Type}
用作资源键。例如:<ControlTheme x:Key="{x:Type Button}" TargetType="Button">
- 如果您希望控件主题应用于选定的控件实例,请使用任何其他内容作为资源键,并使用
{StaticResource}
查找此资源。通常,此键将是一个string
。
信息
请注意,这意味着一次只能应用一个控件主题到一个控件。
示例:使所有按钮都成为圆形
我们可以通过简单地将控件主题的 x:Key
更改为匹配 Button
类型来将我们的新控件主题应用到应用程序中的所有按钮。
App.axaml
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="AvaloniaApplication.App">
<Application.Styles>
<FluentTheme />
</Application.Styles>
<Application.Resources>
<ControlTheme x:Key="{x:Type Button}" TargetType="Button">
<Setter Property="Background" Value="Blue"/>
<Setter Property="Foreground" Value="Yellow"/>
<Setter Property="Padding" Value="8"/>
<Setter Property="Template">
<ControlTemplate>
<Panel>
<Ellipse Fill="{TemplateBinding Background}"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"/>
<ContentPresenter x:Name="PART_ContentPresenter"
Content="{TemplateBinding Content}"
Margin="{TemplateBinding Padding}"/>
</Panel>
</ControlTemplate>
</Setter>
<Style Selector="^:pointerover">
<Setter Property="Background" Value="Red"/>
<Setter Property="Foreground" Value="White"/>
</Style>
</ControlTheme>
</Application.Resources>
</Application>
TargetType
ControlTheme.TargetType
属性指定适用 Setter
属性的类型。如果您没有指定 TargetType
,则必须使用类名限定 Setter
对象中的属性,使用 Property="ClassName.Property"
的语法。例如,不要设置 Property="FontSize"
,而应该设置 Property
为 TextBlock.FontSize
或 Control.FontSize
。
更多资源
- ButtonCustomize 示例中有一个
WinClassicButtonTheme
。 您可以在以下位置查看 Avalonia 内置控件的控件主题:- Simple Theme
- Fluent Theme
Fluent 主题
Introduction
Avalonia Fluent 主题受到微软的 Fluent Design System 的启发,该系统是一组用于创建视觉吸引力和交互式用户界面的设计指南和组件。Fluent Design System 强调现代、清晰的美学,平滑的动画和直观的交互。它在不同平台上提供了一致而精致的外观和感觉,同时为开发人员提供了我们的样式系统的灵活性。
如何使用
首先,需要安装 Avalonia.Themes.Fluent NuGet 包。
信息
关于如何添加 NuGet 包,您可以参考 NuGet 页面或 Visual Studio、Rider 文档中的步骤。
然后,将主题包含在 Application 类中:
App.axaml
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="AvaloniaApplication.App">
<Application.Styles>
<FluentTheme />
</Application.Styles>
</Application>
备注
如果需要指定主题为暗色或浅色变体,请参阅主题变体文档。
更改主题密度
Fluent 主题有两套预定义的密度变体。 要切换到更紧凑的外观,可以通过 DensityStyle
属性设置:
App.axaml
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="AvaloniaApplication.App">
<Application.Styles>
<FluentTheme DensityStyle="Compact" />
</Application.Styles>
</Application>
创建自定义颜色调色板
虽然 FluentTheme 有内置的暗色和浅色变体资源,但仍然可以重写这些变体的基础调色板。 这在开发人员想要使用相同的基本主题但具有不同颜色时非常有用。
要实现这一点,需要定义:
App.axaml
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="AvaloniaApplication.App">
<Application.Styles>
<FluentTheme>
<FluentTheme.Palettes>
<!-- 适用于浅色主题变体的调色板 -->
<ColorPaletteResources x:Key="Light" Accent="Green" RegionColor="White" ErrorText="Red" />
<!-- 适用于暗色主题变体的调色板 -->
<ColorPaletteResources x:Key="Dark" Accent="DarkGreen" RegionColor="Black" ErrorText="Yellow" />
</FluentTheme.Palettes>
</FluentTheme>
</Application.Styles>
</Application>
虽然 ColorPaletteResources
具有许多可以单独为每个变体重写的颜色属性,但是只能重新定义所需的最小集合,其他所有内容保持默认值。如上面的示例中,仅覆盖了几种颜色。
如果未重写 Accent,Avalonia 将使用平台 OS 的强调颜色(如果可用)。 另外,Accent 支持绑定,并且可以在运行时更改,但其他属性不支持,因为它们在应用程序启动后被读取一次,并且为了性能原因而静态使用。
可以在代码中构建调色板,但是同样的规则适用——只有 Accent 可以在运行时更新,并且调色板应该在样式或主题加载后设置为不可变。
备注
FluentTheme 仅支持暗色和浅色主题变体,不支持为自定义变体定义调色板。
使用在线编辑器创建自定义颜色调色板
Microsoft Fluent Theme Editor 已移植到 Avalonia,现在也可以与我们的 FluentTheme 一起使用。 它可在 FluentEditor 页面上使用,并支持以下功能:
- 编辑浅色和暗色变体的调色板颜色。
- 预览当前调色板。
- 将当前调色板导出为可以复制粘贴到
App.axaml
文件中的 XAML 代码。 - 将当前颜色保存在 JSON 文件中,并从文件系统加载它。
- 在调色板的颜色之间有低对比度时,自动提示。
- 提供几个快速启动预设。
以下是使用 Forest 调色板预设的 FluentTheme 示例:
Simple 主题
简介
Avalonia Simple 主题专门设计为简约且轻量,具有有限的内置样式。它为构建自定义样式提供了简单干净的基础。低视觉和结构复杂性使其成为在嵌入式设备上运行的应用程序的理想选择。
如何使用
首先,需要安装 Avalonia.Themes.Simple NuGet 包。
信息
关于如何添加 NuGet 包,您可以参考 NuGet 页面或 Visual Studio、Rider 文档中的步骤。
然后,将主题包含在 Application 类中:
App.axaml
<Application xmlns="https://github.com/avaloniaui"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
x:Class="AvaloniaApplication.App">
<Application.Styles>
<SimpleTheme />
</Application.Styles>
</Application>