随着社会的发展,大家对软件的要求,从最初的命令行输入输出,到可视化输入输出,如报表,图表等;从最初的可用性,稳定性为主,到现代软件理念中的便捷易用性转变,在保证稳定可用外,对软件的交互易用要求越来越高,而这些则离不开UI设计以及样式的应用。今天以一些简单的小例子,简述Avalonia UI中样式与主题的应用,仅供学习分享使用,如有不足之处,还请指正。
样式概述
Avalonia UI中的样式是一种可以在控件之间共享属性设置的机制。与WPF中的样式不同,Avalonia UI中的样式设置借鉴了B/S开发中的CSS样式理念;不过Avalonia UI引入了主题(ControlTheme)和WPF中的样式相对应。当给UI控件设置样式时,样式系统从控件开始沿着逻辑树向上搜索,直到应用程序的最高级别(App.axaml)。如果样式系统匹配到控件设置的样式时,则根据样式中的设置器互进行更改。样式系统沿着逻辑树向上搜索,采用就近原则,所以如果有多个匹配项,则离控件更近的地方定义的样式可以覆盖离控件较远的地方定义的样式。
样式定义
样式的定义包含两部分:选择器属性(Selector)和一个或多个设置器元素(Setter)。选择器的值包含使用Avalonia UI“样式选择器语法”的字符串。每个设置器元素通过名称标识将被更改的属性和将被替换的新值。语法如下:
<Style Selector="selector syntax"><Setter Property="property name" Value="new value"/>...
</Style>
说明:Avalonia UI 样式选择器语法 类似于 CSS(层叠样式表)中使用的语法。
样式放置在UserControl或Window或者Application上的Styles集合中,如下所示:
<UserControl.Styles><Style Selector="Button"><Setter Property="Background" Value="Red"></Setter><Setter Property="Margin" Value="10"></Setter></Style>
</UserControl.Styles>
<StackPanel><Button Width="100" Height="100" Content="按钮"></Button><Button Width="100" Height="100" Content="按钮"></Button>
</StackPanel>
上述样式示例,定义了一个Button按钮的样式(背景色为红色,边距为10px),选择器属性为Button,表示在UserControl范围内的Button都会应用此样式,如StackPanel中的Button,示例效果如下所示:
选择器定义样式将作用于哪些控件,定义选择器的格式有很多种,类型选择器是比较常用的一种方式。设置器描述了当选择器与控件匹配时会发生什么,设置器主要设置控件的属性Property和值Value。当样式与控件匹配时,样式中所有的设置器都将应用于控件。
嵌套样式
在实际应用用,除了可以定义单独的样式,还可以定义嵌套样式,而嵌套样式只需要将子样式作为Style元素的子元素包含,并在子选择器的开头加上“嵌套选择器标识符^”。如下所示:
<UserControl 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"xmlns:vm="clr-namespace:FirstAvalonia.ViewModels"mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"x:Class="FirstAvalonia.Views.MainView"><UserControl.Styles><Style Selector="TextBlock.h1"><Setter Property="FontSize" Value="24"/><Setter Property="FontWeight" Value="Bold"/><Style Selector="^:pointerover"><Setter Property="Foreground" Value="Red"/></Style></Style></UserControl.Styles><StackPanel><TextBlock Classes="h1" Text="字号24,加粗,鼠标放上变红色"></TextBlock></StackPanel>
</UserControl>
在上述示例中,子样式继承了父样式的属性设置,当鼠标悬停在TextBlock.h1设置的样式时,子样式才会生效。如下:
注意,"嵌套选择器标识符^"必须存在,并且必须出现在子选择器的开头
样式键
样式选择器匹配的对象的类型并不是由控件的具体类型决定的,而是通过检查器StyleKey属性来确定。默认情况下,StyleKey属性返回当前实例的类型。示例:当你自定义一个控件,且它继承自Button,如果你希望它被样式化一个按钮,可以重写StyleKeyOverride属性,并让它返回typeof(Button)。如下所示:
public class MyButton : Button
{// MyButton 将会被作为标准的 Button 控件样式化。protected override Type StyleKeyOverride => typeof(Button);
}
在WPF/UWP中,当你派生一个新的控件时,它将被样式化为其基础控件,除非你覆盖了DefaultStyleKey属性。在Avalonia UI中,控件将使用其具体类型进行样式化,除非提供了相同的样式键。
样式类
在Avalonia UI中,可以为控件分配一个或多个样式类,并使用它们来指导样式选择。样式类通过在控件元素中使用Classes属性进行分配。多个样式类之间用空格进行分隔。示例如下所示:
<UserControl 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"xmlns:vm="clr-namespace:FirstAvalonia.ViewModels"mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"x:Class="FirstAvalonia.Views.MainView"><UserControl.Styles><Style Selector="Button.h1"><Setter Property="FontSize" Value="24"></Setter></Style><Style Selector="Button.blue"><Setter Property="Background" Value="Blue"></Setter></Style></UserControl.Styles><StackPanel><Button Classes="h1 blue" Width="100" Height="100" Content="按钮"></Button><Button Width="100" Height="100" Content="按钮"></Button></StackPanel>
</UserControl>
在上述示例中,第一个Button应用了h1,blue两个样式,第二个按钮没有应用样式,差别如下:
注意,选择器中样式类的定义必须以“控件类型.样式类名称”的格式进行定义。多个样式类定义必须用点号分隔,如“Button.larger.blue”等。如果选择器指定了多个类,则控件必须同时拥有所有请求的类定义才能匹配。
伪类(Pseudo Classes)
伪类(Pseudo Classes)与CSS类似,控件可以拥有伪类,这些类是控件本身而不是用户定义的。伪类在选择器中的名称,始终以“冒号”开头。如:pointerover伪类表示鼠标悬停在控件上,pressed伪类表示鼠标在按钮上按下,checked表示复选框选中时。示例如下所示:
<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>
控件主题
控件主题是在样式的基础上构建的,用于为控件创建可切换的主题。控件主题本质是样式,但和样式有一些区别:
- 控件主题没有选择器,它们有一个TargetType属性,用于描述它们要针对的控件。
- 控件主题存储在ResourceDictionary中,而不是Styles集合中。
- 控件主题通过设置Theme属性来分配给控件,通常使用StaticResource标记扩展。
控件主题通常用于模板化控件,对于非模板化的控件,通常使用标准样式更方便。
定义主题,控件主题作为资源的形式出现,一般可以在窗口(Window),用户控件(UserControl),应用程序(Application)的Resources中进行定义,如下所示:
<Application xmlns="https://github.com/avaloniaui"xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"x:Class="FirstAvalonia.App"RequestedThemeVariant="Default"><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}"VerticalAlignment="Center" HorizontalAlignment="Center" /></Panel></ControlTemplate></Setter></ControlTheme></Application.Resources>
</Application>
在上述示例中,ControlTheme作为Resources的子元素,x:Key表示资源的唯一标识,TargetType表示主题要应用的控件类型。
应用主题,主题定义完成后,可以在控件上通过Theme="{StaticResource ResourceKey}"的形式进行引用,如下所示:
<StackPanel Orientation="Horizontal"><Button Classes="h1 blue" Width="100" Height="100" Content="按钮" Theme="{StaticResource EllipseButton}"></Button><Button Width="100" Height="100" Content="按钮" ></Button>
</StackPanel>
应用主题后的效果如下所示:
控件主题查找
控件主题有两种查找方式:
如果控件的Theme属性被设置,则应用该控件主题;如果没有指定控件的Theme属性,Avalonia UI会从逻辑树向上搜索控件,查找一个x:Key与控件的样式键匹配的ControlTheme资源。所以就有两种方式来定义控件主题:
- 如果希望控件主题应用于控件的所有实例,则将主题资源的x:Key属性设置为{x:Type}作为资源的标识,如:<ControlTheme x:Key="{x:Type Button}" TargetType="Button">。
- 如果希望控件主题应用于选定的控件实例,则需要使用普通文本内容作为资源键,并使用StaticResource查找资源。
ControlTheme中的TargetType属性指定适用于Setter属性的类型。如果没有指定TargetType,则必须使用类名限定Setter对象中的属性,使用Property="ClassName.Property"
的语法。
以上就是《Avalonia系列文章之样式与主题》的全部内容,旨在抛砖引玉,一起学习,共同进步。