Avalonia的样式系统是XAML和CSS样式方法的结合,因此只了解其中一种技术的开发人员可能会对另一种技术的细节感到困惑。
让我们设想一个问题,即控件上的一个或多个样式设置器未应用。下面我们将列出常见的可能原因和解决方案。
选择器的目标控件不存在
Avalonia选择器(类似于CSS选择器)在没有任何匹配的控件时不会引发任何错误或警告。这包括使用不存在的名称或类,或者在没有子级可以匹配内部选择器时使用子选择器。原因很简单,一个样式可以针对许多在运行时创建或删除的控件,因此无法验证选择器。
目标属性被另一个样式覆盖
样式是按照声明的顺序应用的。如果有多个样式文件针对相同的控件属性,一个样式可以覆盖另一个样式:
<Style Selector="TextBlock.header">
<Style Property="Foreground" Value="Green" />
</Style>
<Style Selector="TextBlock.header">
<Style Property="Foreground" Value="Blue" />
<Style Property="FontSize" Value="16" />
</Style>
<StyleInclude Source="Style1.axaml" />
<StyleInclude Source="Style2.axaml" />
这里首先应用了来自文件Styles1.axaml的样式,因此来自文件Styles2.axaml的样式中的设置器具有优先级。结果中的TextBlock将具有FontSize="16"和Foreground="Green"。在样式文件中也会发生相同的顺序优先级。
本地设置属性会覆盖样式
与WPF类似,Avalonia属性可以具有多个值,通常具有不同的优先级。
在此示例中,您可以看到本地值(直接在控件上定义)的优先级高于样式值,因此文本块将具有红色前景色:
<TextBlock Classes="header" Foreground="Red" />
...
<Style Selector="TextBlock.header">
<Setter Property="Foreground" Value="Green" />
</Style>
缺少样式伪类(触发器)选择器
假设您可能希望第二个样式覆盖前一个样式,但实际上没有覆盖的情况:
<Style Selector="Border:pointerover">
<Setter Property="Background" Value="Blue" />
</Style>
<Style Selector="Border">
<Setter Property="Background" Value="Red" />
</Style>
...
<Border Width="100" Height="100" Margin="100" />
在这个代码示例中,Border
在正常情况下具有红色背景,当鼠标悬停在上面时具有蓝色背景。这是因为与CSS一样,更具体的选择器具有优先级。当您希望使用单个样式覆盖任何状态(pointerover、pressed或其他状态)的默认样式时,这是一个问题。为了实现这一点,您需要为这些状态创建新的样式。
带有伪类的选择器无法覆盖默认样式
下面是一个可以预期在默认样式之上工作的样式代码示例:
<Style Selector="Button">
<Setter Property="Background" Value="Red" />
</Style>
<Style Selector="Button:poinverover">
<Setter Property="Background" Value="Blue" />
</Style>
您可能期望Button
在默认情况下为红色,在鼠标悬停在上面时为蓝色。实际上,只有第一个样式的设置器将被应用,第二个样式将被忽略。
<Style Selector="Button">
<Setter Property="Background" Value="{DynamicResource ButtonBackground}"/>
<Setter Property="Template">
<ControlTemplate>
<ContentPresenter Name="PART_ContentPresenter"
Background="{TemplateBinding Background}"
Content="{TemplateBinding Content}"/>
</ControlTemplate>
</Setter>
</Style>
<Style Selector="Button:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="{DynamicResource ButtonBackgroundPointerOver}" />
</Style>
实际的背景是由ContentPresenter
渲染的,在默认情况下,它绑定到按钮的Background
属性。然而,在鼠标悬停状态下,选择器直接将背景应用于ContentPresenter
(Button:pointerover /template/ ContentPresenter#PART_ContentPresenter)。这就是为什么在前面的代码示例中我们的设置器被忽略的原因。更正后的代码也应该直接针对内容呈现器:
<!-- 这里 #PART_ContentPresenter 名称选择器不是必需的,但添加它可以获得更具体的样式 -->
<Style Selector="Button:pointerover /template/ ContentPresenter#PART_ContentPresenter">
<Setter Property="Background" Value="Blue" />
</Style>
当样式不再应用时,无法恢复特定属性的先前值
在Avalonia中,我们有多种类型的属性,其中之一是Direct Property,根本不支持样式化。这些属性以简化的方式工作,以实现更低的开销和更高的性能,并且不存储根据优先级的多个值。相反,只保存最新的值,并且无法恢复。您可以在这里找到有关属性的更多详细信息。