样式

Avalonia中的样式用于在控件之间共享属性设置。Avalonia的样式系统可以被看作是CSS样式和WPF/UWP样式的混合。最基本的样式由选择器(selector)和设置器(setters)的集合组成。

样式适用于它定义的控件以及所有的后代控件。

以下样式选择带有h1样式类(style class)的任何TextBlock,并将其字体大小设置为24点,字体加粗。

<Style Selector="TextBlock.h1">
    <Setter Property="FontSize" Value="24"/>
    <Setter Property="FontWeight" Value="Bold"/>
</Style>

通过将样式添加到Control.StylesApplication.Styles集合,可以在任何控件或Application对象上定义样式。

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Window.Styles>
        <Style Selector="TextBlock.h1">
            <Setter Property="FontSize" Value="24"/>
            <Setter Property="FontWeight" Value="Bold"/>
        </Style>
    </Window.Styles>

    <TextBlock Classes="h1">I'm a Heading!</TextBlock>
</Window>

还可以使用StyleInclude类从其他文件中引用样式,例如:

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Window.Styles>
        <StyleInclude Source="/CustomStyles.xaml" />
    </Window.Styles>

    <TextBlock Classes="h1">I'm a Heading!</TextBlock>
</Window>

其中CustomStyles.xaml是一个XAML文件,它的根为StyleStyles,并作为asset引用在应用程序中,例如:

CustomStyles.xaml

<Styles xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Style Selector="TextBlock.h1">
        ...
    </Style>
</Styles>

请注意,与WPF/UWP不同,如果将样式添加到控件或应用程序ResourceDictionary中,则样式将不起作用。这是因为在Avalonia中定义样式的顺序很重要,而ResourceDictionary是一个无序字典。

样式类(Style Class)

与CSS一样,控件可以被赋予样式类,这些类可以在选择器中使用。通过将Classes属性设置为以空格分隔的字符串列表,可以在XAML中分配样式类。以下示例将h1blue样式类作用于Button

<Button Classes="h1 blue"/>

如果需要按条件添加或删除类,可以使用以下特殊语法:

<Button Classes.accent="{Binding IsSpecial}" />

还可以使用Classes集合在代码中控制样式类:

control.Classes.Add("blue");
control.Classes.Remove("red");

伪类(Pseudoclasses)

与CSS一样,控件也可以有伪类。这些是由控件本身而不是用户定义的类。伪类以:字符开头。

一个伪类的例子,:pointerover(类似于CSS中的:hover)。

伪类提供WPF中的Triggers和UWP中的VisualStateManager功能:

<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等。

自定义伪类(Custom PseudoClasses)

您可以为CustomControlTemplatedControl创建自己的伪类。 下面的函数根据StyledElement上的布尔值添加或删除伪类。

PseudoClasses.Set(":className", bool);

记住: 伪类总是以:开头 !

选择器(Selectors)

选择器使用自定义选择器语法选取控件,该语法与CSS选择器使用的语法非常相似。一些选择器的示例:

选择器描述

Button

选取所有Button控件

Button.red

选取所有带有red样式类的Button控件

Button.red.large

选取所有带有redlarge样式类的Button控件

Button:focus

选取所有带有:focus伪类的Button控件

Button.red:focus

选取所有带有red样式类和:focus伪类的Button控件

Button#myButton

选取一个命名为myButtonButton控件

StackPanel Button.foo

选取在StackPanel的后代中带有foo类的所有Button控件

StackPanel > Button.foo

选取在StackPanel的直接子代中带有foo类的所有Button控件

Button /template/ ContentPresenter

选取Button的模板内所有ContentPresenter控件

想要了解更多信息,请浏览选择器文档.

设置器(Setters)

样式的设置器描述了当选择器与控件匹配时会发生什么。它们是简单的属性-值对,格式如下:

<Setter Property="FontSize" Value="24"/>
<Setter Property="Padding" Value="4 2 0 4"/>

您还可以使用复杂元素语法来声明更复杂的对象值:

<Setter Property="MyProperty">
   <MyObject Property1="My Value"/>
</Setter>

绑定也可以应用在设置器(Setter)上,可以绑定到目标控件的DataContext

<Setter Property="FontSize" Value="{Binding SelectedFontSize}"/>

每当样式与控件匹配时,所有设置器都将应用于该控件。如果样式选择器导致样式不再与控件匹配,则该属性值将恢复为其下一个最高优先级的值。

注意:Setter创建了一个Value的单一实例,它将被应用于样式匹配的所有控件:如果对象是可变的,那么变化将反映在所有控件上。继而,在设置器Value内的任何对象的绑定将不能访问目标控件的DataContext,因为可能有多个目标控件。

<Style Selector="local|MyControl">
  <Setter Property="MyProperty">
     <MyObject Property1="{Binding MyViewModelProperty}"/>
  </Setter>
</Style>

在上面的例子中,绑定源是MyObject.DataContext而不是MyControl.DataContext,如果MyObject没有数据上下文,那么绑定将不能产生值。

注意:在这种情况下,如果您正在使用已编译的绑定,则需要在<Style>元素中显式设置绑定源的数据类型:

<Style Selector="MyControl" x:DataType="MyViewModelClass">
  <Setter Property="ControlProperty" Value="{Binding MyViewModelProperty}" />
</Style>

设置器中的模板

如上所述,通常会创建设置器的Value的单个实例,并在所有匹配控件之间共享。因此,要将控件作为设置器的值,必须将控件包装在<Template>中:

<Style Selector="Border.empty">
  <Setter Property="Child">
    <Template>
      <TextBlock>No content available.</TextBlock>
    </Template>
  </Setter>
</Style>

样式优先级

如果多个样式与一个控件匹配,并且它们都试图设置相同的属性,则该控件优先使用最近的样式。如下示例:

<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Window.Styles>
        <Style Selector="TextBlock.h1">
            <Setter Property="FontSize" Value="24"/>
            <Setter Property="FontWeight" Value="Bold"/>
        </Style>
    </Window.Styles>

    <StackPanel>
        <StackPanel.Styles>
            <Style Selector="TextBlock.h1">
                <Setter Property="FontSize" Value="48"/>
                <Setter Property="Foreground" Value="Red"/>
            </Style>
        </StackPanel.Styles>

        <TextBlock Classes="h1">
            <StackPanel.Styles>
                <Style Selector="TextBlock.h1">
                    <Setter Property="Foreground" Value="Blue"/>
                </Style>
            </StackPanel.Styles>

            I'm a Heading!
        </TextBlock>
    </StackPanel>
</Window>

这里在多个地方定义了h1样式。TextBlock最后将使用以下设置:

属性

FontSize

48

StackPanel

FontWeight

Bold

Window

Foreground

Blue

TextBlock

如果一个属性应用了多个样式设置器,则优先级为:

  • 在最接近控件的祖先中定义的样式的值

  • 对于同一Styles集合中声明的两种样式,显示更靠后的样式

最后更新于