设置器优先级
Avalonia 的 Setter
按照 BindingPriority
、视觉树局部性和样式集合顺序依次应用。每个 StyledProperty
都单独应用优先级,从而使得样式可以受益于组合。DirectProperty
和 CLR 属性不能被样式化,因此不参与此优先级。
绑定优先级
Animation = -1, // 最高优先级
LocalValue = 0,
StyleTrigger,
Template,
Style,
Inherited,
Unset = int.MaxValue, // 最低优先级
如何在XAML中分配绑定优先级?
BindingPriority
不能在 XAML 中显式设置。以下示例展示了如何在每个场景中隐式分配 绑定优先级
。这对于设计和排除样式故障至关重要,以确保样式按预期工作。
Animation
Animation
具有最高的 绑定优先级
,并应用于 Keyframe
中的 Setter
,通常在整个过渡系统中应用。
<Button Background="Green" Content="Bounces from Red to Blue">
<Button.Styles>
<Style Selector="Button">
<Style.Animations>
<Animation IterationCount="Infinite" Duration="0:0:2">
<KeyFrame Cue="0%">
<Setter Property="Background" Value="Red" />
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="Background" Value="Blue" />
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
</Button.Styles>
</Button>
LocalValue
当 XAML 属性在 ControlTemplate
之外直接设置时分配。下面的两个 Background
Setter
都将具有 LocalValue
优先级。
<Button Background="Orange" />
<Button Background="{DynamicResource ButtonBrush}" />
资源标记扩展对优先级没有任何影响。
StyleTrigger
当 Selector
具有条件激活时,Setter
的 绑定优先级
从 Style
提升到 StyleTrigger
。任何具有条件激活的两个选择器将具有相同的优先级,无论存在多少激活器以及激活器在选择器语法中的位置。Avalonia 没有 CSS 的 Specificity 概念。
<Style Selector="Button:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="Orange" />
</Style>
样式类、伪类、子位置和属性匹配选择器是条件性的。控件名称选择器不是条件性的。
Template
当属性在 ControlTemplate
中直接设置时。下面的 BorderThickness
、Background
和 Padding
具有 Template
优先级。
<ControlTemplate>
<Border BorderThickness="2">
<Button Background="{DynamicResource ButtonBrush}" Padding="{TemplateBinding Padding}" />
</Border>
</ControlTemplate>
Style
当 Setter
在 Style
中定义且没有条件激活时。
<Style Selector="Button /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="Orange" />
</Style>
特别值得注意的是,其优先级低于 Template
。因此,这些选择器不能用于覆盖上述 Template
示例中提到的属性。
Inherited
当属性未设置时,它可能会从其父级继承属性值。这必须在属性注册期间或在 OverrideMetadata
中指定。
public static readonly StyledProperty<bool> UseLayoutRoundingProperty =
AvaloniaProperty.Register<Layoutable, bool>(
nameof(UseLayoutRounding),
defaultValue: true,
inherits: true);
视觉树局部性
具有相同 BindingPriority
的 Setter
将根据它们在视觉树中相对于 Control
的位置来选择。需要向上遍历节点最少的 Setter
将具有优先权。内联样式 Setter
在此步骤中具有最高优先权。
<Window>
<Window.Styles>
<Style Selector="Button">
<Setter Property="FontSize" Value="16" />
<Setter Property="Foreground" Value="Red" />
</Style>
</Window.Styles>
<StackPanel>
<StackPanel.Styles>
<Style Selector="Button">
<Setter Property="FontSize" Value="24" />
</Style>
</StackPanel.Styles>
<Button Content="This Has FontSize=24 with Foreground=Red" />
</StackPanel>
</Window>
Styles集合顺序
当 绑定优先级
和视觉树局部性都相等时,最终的决定因素是 Styles
集合中的顺序。最后一个适用的 Setter
将具有优先级。
<StackPanel>
<StackPanel.Styles>
<Style Selector="Button.small">
<Setter Property="FontSize" Value="12" />
</Style>
<Style Selector="Button.big">
<Setter Property="FontSize" Value="24" />
</Style>
</StackPanel.Styles>
<Button Classes="small big" Content="This Has FontSize=24" />
<Button Classes="big small" Content="This Also Has FontSize=24" />
</StackPanel>
这些按钮以不同的顺序指定它们的类,但这不会影响 Setter 优先级。
绑定优先级不会传递
回想一下上面的 Animation
示例。即使 BindingPriority.Animation
具有最高优先级,当你悬停时,动画背景仍会被静态背景替换。这是因为 Selector
目标了错误的 Control
。检查 ControlTheme
是诊断原因的必要步骤。

<ControlTheme x:Key="{x:Type Button}" TargetType="Button">
<Setter Property="Background" Value="{DynamicResource ButtonBackground}"/>
<Setter Property="Template">
<ControlTemplate>
<ContentPresenter x:Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"/>
</ControlTemplate>
</Setter>
<Style Selector="^:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundPointerOver}"/>
</Style>
</ControlTheme>
顶部的 Setter
以 Style
优先级将 ButtonBackground
应用到 Button
。Background
的渲染由具有 Template
优先级的 ContentPresenter
处理,它获取已应用于 Button
的 ButtonBackground
。
但是当 Button
被悬停时,:pointerover
Selector
会被激活并具有 StyleTrigger
优先级,这会覆盖 TemplateBinding
并获取 ButtonBackgroundPointerOver
而不是 Button
的 Background
。这绕过了我们最初的 Animation
Selector
所针对的 Button
的 Background
。
以下表格总结了这一点:
悬停时的背景 Setter 和样式 | 优先级 | 位置 |
---|---|---|
LocalValue | Button | |
Background="Red" | Animation (覆盖 LocalValue) | Keyframe |
<ContentPresenter Background="{TemplateBinding Background}"/> | Template | ControlTemplate |
^:pointerover /template/ ContentPresenter#PART_ContentPresenter | StyleTrigger (覆盖 Template) | ControlTheme |
相反,我们应该使用优先级至少为 StyleTrigger
的 Setter
来针对 ContentPresenter
。BindingPriority.Animation
满足这个要求。这一观察结果只有在检查原始 ControlTemplate
时才能得出,并强调仅依赖优先级是不足以有效样式化应用程序的。
<Button Background="Green" Content="Bounces from Red to Blue">
<Button.Styles>
<Style Selector="Button /template/ ContentPresenter#PART_ContentPresenter">
<Style.Animations>
<Animation IterationCount="Infinite" Duration="0:0:2">
<KeyFrame Cue="0%">
<Setter Property="Background" Value="Red" />
</KeyFrame>
<KeyFrame Cue="100%">
<Setter Property="Background" Value="Blue" />
</KeyFrame>
</Animation>
</Style.Animations>
</Style>
</Button.Styles>
</Button>
