跳到主要内容
版本:11.0.0

设置器优先级

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 中直接设置时。下面的 BorderThicknessBackgroundPadding 具有 Template 优先级。

<ControlTemplate>
<Border BorderThickness="2">
<Button Background="{DynamicResource ButtonBrush}" Padding="{TemplateBinding Padding}" />
</Border>
</ControlTemplate>

Style

SetterStyle 中定义且没有条件激活时。

<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);

视觉树局部性

具有相同 BindingPrioritySetter 将根据它们在视觉树中相对于 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 for Button, Trimmed
<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>

顶部的 SetterStyle 优先级将 ButtonBackground 应用到 ButtonBackground 的渲染由具有 Template 优先级的 ContentPresenter 处理,它获取已应用于 ButtonButtonBackground

但是当 Button 被悬停时,:pointerover Selector 会被激活并具有 StyleTrigger 优先级,这会覆盖 TemplateBinding 并获取 ButtonBackgroundPointerOver 而不是 ButtonBackground。这绕过了我们最初的 Animation Selector 所针对的 ButtonBackground。 以下表格总结了这一点:

悬停时的背景 Setter 和样式优先级位置
Background="Green"LocalValueButton
Background="Red"Animation (覆盖 LocalValue)Keyframe
<ContentPresenter Background="{TemplateBinding Background}"/>TemplateControlTemplate
^:pointerover /template/ ContentPresenter#PART_ContentPresenterStyleTrigger (覆盖 Template)ControlTheme

相反,我们应该使用优先级至少为 StyleTriggerSetter 来针对 ContentPresenterBindingPriority.Animation 满足这个要求。这一观察结果只有在检查原始 ControlTemplate 时才能得出,并强调仅依赖优先级是不足以有效样式化应用程序的。

Corrected to override :pointerover priority
<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>