隐含的样式
ResourceDictionary中的每个条目都需要一个字典键。这是一个不争的事实。如果您尝试将null键传递给ResourceDictionary对象的Add方法,则会引发ArgumentNullException。
但是,有一种特殊情况,程序员不需要提供此字典密钥。而是自动生成字典密钥。
这种特殊情况适用于添加到ResourceDictionary而没有x:Key设置的Style对象。 ResourceDictionary基于TargetType生成密钥,该密钥始终是必需的。 (稍微探索一下,这个特殊的字典键是与Style的TargetType相关联的完全限定名。例如,对于Button的TargetType,字典键是“Xamarin.Forms.Button”。但是你没有需要知道。)
您还可以在没有代码中的字典键的情况下向ResourceDictionary添加样式:Add方法的重载接受Style类型的参数,但不需要任何其他参数。
ResourceDictionary中具有这些生成的键之一的Style对象称为隐式样式,生成的字典键非常特殊。您不能使用StaticResource直接引用此密钥。但是,如果ResourceDictionary范围内的元素与字典键具有相同的类型,并且该元素没有将其Style属性显式设置为另一个Style对象,则会自动应用此隐式样式。
ImplicitStyle项目中的以下XAML演示了这一点。它与BasicStyle XAML文件相同,只是Style没有x:Key设置,并且按钮上的Style属性未使用StaticResource设置:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ImplicitStyle.ImplicitStylePage">
<ContentPage.Resources>
<ResourceDictionary>
<Style TargetType="Button">
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="VerticalOptions" Value="CenterAndExpand" />
<Setter Property="BorderWidth" Value="3" />
<Setter Property="TextColor" Value="Red" />
<Setter Property="FontSize" Value="Large" />
<Setter Property="BackgroundColor">
<Setter.Value>
<OnPlatform x:TypeArguments="Color"
Android="#404040" />
</Setter.Value>
</Setter>
<Setter Property="BorderColor">
<Setter.Value>
<OnPlatform x:TypeArguments="Color"
Android="White"
WinPhone="Black" />
</Setter.Value>
</Setter>
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<Button Text=" Carpe diem " />
<Button Text=" Sapere aude " />
<Button Text=" Discere faciendo " />
</StackLayout>
</ContentPage>
尽管按钮和样式之间没有任何明确的联系,但样式仍然适用:
仅当元素的类与Style的TargetType完全匹配时才应用隐式样式。如果在StackLayout中包含从Button派生的元素,则不会应用Style。
您可以使用本地属性设置来覆盖通过隐式样式设置的属性,就像您可以使用StaticResource覆盖样式集中的属性设置一样。
您会发现隐式样式非常强大且非常有用。每当您有多个相同类型的视图并且确定您希望它们都具有相同的属性设置或两个时,就可以很容易地快速定义隐式样式。你不必触摸元素吗?自我。
然而,强大的力量至少有一些程序员的责任。因为在元素本身中没有引用样式,所以在简单地检查XAML以确定某些元素是否被设置样式时可能会造成混淆。有时页面的外观表明隐式样式应用于某些元素,但是定义隐式样式的位置并不明显。如果您想要更改该隐式样式,则必须在可视树中手动搜索它。
因此,您应该将隐式样式定义为尽可能接近它们应用的元素。如果获取隐式样式的视图位于特定的StackLayout中,则在该StackLayout的Resources部分中定义隐式样式。一两条评论也可能有助于避免混淆。
有趣的是,隐式样式有一个内置的限制,可以说服你让它们接近它们应用的元素。这是限制:您可以使用显式字典键从Style派生隐式样式,但不能反过来。您不能使用BasedOn来引用隐式样式。
如果定义使用BasedOn相互派生的样式链,则隐式样式(如果有)始终位于链的末尾。没有进一步的推导是可能的。
这意味着您可以使用三种类型的层次结构来构建样式:
- 从应用程序和页面上定义的样式到可视树中较低布局上定义的样式。
- 从为基类定义的样式(如VisualElement和View)到为特定类定义的样式。
- 从具有显式字典键的样式到隐式样式。
这在StyleHierarchy项目中得到了证明,该项目使用了类似(但稍微简化)的样式集,如之前在StyleInheritance项目中所见。 但是,这些样式现在分布在三个Resources部分。
使用您在第10章的ResourceTrees程序中看到的技术,StyleHierarchy pro?ject被赋予了一个基于XAML的App类。 App.xaml类有一个ResourceDictionary,其中包含只有一个属性setter的样式:
<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="StyleHierarchy.App">
<Application.Resources>
<ResourceDictionary>
<Style x:Key="visualStyle" TargetType="VisualElement">
<Setter Property="BackgroundColor" Value="Pink" />
</Style>
</ResourceDictionary>
</Application.Resources>
</Application>
在多页面应用程序中,此样式将在整个应用程序中使用。
App类的代码隐藏文件调用InitializeComponent来处理XAML文件并设置MainPage属性:
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new StyleHierarchyPage();
}
__
}
页面类的XAML文件为从App类中的样式派生的整个页面定义了一个样式,并且还为从页面样式派生的两个隐式样式定义了样式。 请注意,页面的Style属性设置为App类中定义的Style:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="StyleHierarchy.StyleHierarchyPage"
Style="{StaticResource visualStyle}">
<ContentPage.Resources>
<ResourceDictionary>
<Style x:Key="baseStyle" TargetType="View"
BasedOn="{StaticResource visualStyle}">
<Setter Property="HorizontalOptions" Value="Center" />
<Setter Property="VerticalOptions" Value="CenterAndExpand" />
</Style>
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<StackLayout.Resources>
<ResourceDictionary>
<Style TargetType="Label"
BasedOn="{StaticResource baseStyle}">
<Setter Property="TextColor" Value="Black" />
<Setter Property="FontSize" Value="Large" />
</Style>
<Style TargetType="Button"
BasedOn="{StaticResource baseStyle}">
<Setter Property="TextColor" Value="Blue" />
<Setter Property="FontSize" Value="Large" />
<Setter Property="BorderColor" Value="Blue" />
<Setter Property="BorderWidth" Value="2" />
</Style>
</ResourceDictionary>
</StackLayout.Resources>
<Button Text=" Carpe diem " />
<Label Text ="A bit of text" />
<Button Text=" Sapere aude " />
<Label Text ="Another bit of text" />
<Button Text=" Discere faciendo " />
</StackLayout>
</ContentPage>
隐式样式被定义为尽可能靠近目标元素。
这是结果:
有时你会有一个带有显式字典键的Style(例如“myButtonStyle”),但你也希望同样的样式也是隐式的。 只需根据该键定义一个样式,而不使用自己的键或设置器:
<Style TargetType="Button"
BasedOn="{StaticResource myButtonStyle}" />
这是一个与myButtonStyle相同的隐式样式。