一本词典树
ResourceDictionary类与其他字典施加相同的规则:字典中的所有项必须具有键,但不允许重复键。
但是,因为VisualElement的每个实例都可能有自己的资源字典,所以您的页面可以包含多个字典,只要每个字典中的所有键都是唯一的,您就可以在不同的字典中使用相同的键。可以想象,可视化树中的每个可视元素都可以拥有自己的字典,但实际上只有资源字典才能应用于多个元素才有意义,因此资源字典只能在Layout或Page对象上找到。
使用此技术,您可以使用字典键构建一个字典树,该字典键可以有效地覆盖其他字典上的键。这在ResourceTrees项目中得到了证明。 ResourceTreesPage类的XAML文件显示了ContentPage的Resources字典,该字典使用horzOptions,vertOptions和textColor的键来定义资源。
对于名为textColor和FontSize的资源,第二个Resources字典附加到内部StackLayout:
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ResourceTrees.ResourceTreesPage">
<ContentPage.Resources>
<ResourceDictionary>
<LayoutOptions x:Key="horzOptions">Center</LayoutOptions>
<LayoutOptions x:Key="vertOptions"
Alignment="Center"
Expands="True" />
<OnPlatform x:Key="textColor"
x:TypeArguments="Color"
iOS="Red"
Android="Pink"
WinPhone="Blue" />
</ResourceDictionary>
</ContentPage.Resources>
<StackLayout>
<Button Text=" Carpe diem "
HorizontalOptions="{StaticResource horzOptions}"
VerticalOptions="{StaticResource vertOptions}"
BorderWidth="{StaticResource borderWidth}"
TextColor="{StaticResource textColor}"
BackgroundColor="{StaticResource backgroundColor}"
BorderColor="{StaticResource borderColor}"
FontSize="{StaticResource fontSize}" />
<StackLayout>
<StackLayout.Resources>
<ResourceDictionary>
<Color x:Key="textColor">Default</Color>
<x:String x:Key="fontSize">Default</x:String>
</ResourceDictionary>
</StackLayout.Resources>
<Label Text="The first of two labels"
HorizontalOptions="{StaticResource horzOptions}"
TextColor="{StaticResource textColor}"
FontSize="{StaticResource fontSize}" />
<Button Text=" Sapere aude "
HorizontalOptions="{StaticResource horzOptions}"
BorderWidth="{StaticResource borderWidth}"
TextColor="{StaticResource textColor}"
BackgroundColor="{StaticResource backgroundColor}"
BorderColor="{StaticResource borderColor}"
FontSize="{StaticResource fontSize}" />
<Label Text="The second of two labels"
HorizontalOptions="{StaticResource horzOptions}"
TextColor="{StaticResource textColor}"
FontSize="{StaticResource fontSize}" />
</StackLayout>
<Button Text=" Discere faciendo "
HorizontalOptions="{StaticResource horzOptions}"
VerticalOptions="{StaticResource vertOptions}"
BorderWidth="{StaticResource borderWidth}"
TextColor="{StaticResource textColor}"
BackgroundColor="{StaticResource backgroundColor}"
BorderColor="{StaticResource borderColor}"
FontSize="{StaticResource fontSize}" />
</StackLayout>
</ContentPage>
内部StackLayout上的Resources字典仅适用于StackLay中的项目,这些是此屏幕截图中间的项目:
以下是它的工作原理:
当XAML解析器在可视元素的属性上遇到StaticResource时,它开始搜索该字典键。它首先在ResourceDictionary中查找该可视元素,如果找不到该键,它将在可视元素的父元素ResourceDictionary中查找键,并在可视树中向上和向上查找,直到它到达页面上的ResourceDictionary。
但是这里缺少一些东西!页面的ResourceDictionary中的条目在哪里用于borderWidth,backgroundColor,borderColor和fontSize?它们不在ResourceTreesPage.xaml文件中!
那些物品在别处。 Application类(每个应用程序的App类定义)也定义了ResourceDictionary类型的Resources属性。这对于定义适用于整个应用程序而不仅仅是特定页面或布局的资源非常方便。当XAML解析器在可视化树中搜索匹配的资源键,并且在该页面的ResourceDictionary中找不到该键时,它最终检查由应用程序类定义的ResourceDictionary。只有在未找到时才会出现针对StaticResource key-not-found错误的XamlParseException。
您可以通过两种方式将项添加到App类的ResourceDictionary对象:
一种方法是在App构造函数中的代码中添加项。确保在实例化主ContentPage类之前执行此操作:
public class App : Application
{
public App()
{
Resources = new ResourceDictionary();
Resources.Add("borderWidth", 3.0);
Resources.Add("fontSize", "Large");
Resources.Add("backgroundColor",
Device.OnPlatform(Color.Default,
Color.FromRgb(0x40, 0x40, 0x40),
Color.Default));
Resources.Add("borderColor",
Device.OnPlatform(Color.Default,
Color.White,
Color.Black));
MainPage = new ResourceTreesPage();
}
}
但是,App类也可以拥有自己的XAML文件,并且可以在该XAML文件的Resources集合中定义应用程序范围的资源。 为此,您需要删除由Xamarin.Forms解决方案模板创建的App.cs文件。 App类没有模板项,因此您需要伪造它。 将新的XAML页面类 - 在Visual Studio中的Forms Xaml页面或Xamarin Studio中的Forms Con?tentPage Xaml添加到项目中。 将其命名为App。 在您之前 - 立即进入App.xaml文件并将根标签更改为Application,然后进入App.xaml.cs文件并将基类更改为Application。
现在您有一个从Application派生的App类,它有自己的XAML文件。 在App.xaml文件中,您可以在Application.Resources属性元素标记中实例化ResourceDictionary并向其添加项目:
<Application xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="ResourceTrees.App">
<Application.Resources>
<ResourceDictionary>
<x:Double x:Key="borderWidth">3</x:Double>
<x:String x:Key="fontSize">Large</x:String>
<OnPlatform x:Key="backgroundColor"
x:TypeArguments="Color"
Android="#404040" />
<OnPlatform x:Key="borderColor"
x:TypeArguments="Color"
Android="White"
WinPhone="Black" />
</ResourceDictionary>
</Application.Resources>
</Application>
代码隐藏文件中的构造函数需要在运行时调用InitializeComponent来解析App.xaml文件,并将这些项添加到字典中。 这应该在正确处理ResourceTreesPage类并将其设置为MainPage属性之前完成:
public partial class App : Application
{
public App()
{
InitializeComponent();
MainPage = new ResourceTreesPage();
}
protected override void OnStart()
{
// Handle when your app starts
}
protected override void OnSleep()
{
// Handle when your app sleeps
}
protected override void OnResume()
{
// Handle when your app resumes
}
}
添加生命周期事件是可选的。
确保在实例化页面类之前调用InitializeComponent。页面类的构造函数调用自己的InitializeComponent来解析页面的XAML文件,而StaticResource标记扩展需要访问App类中的Resources集合。
每个Resources字典都有一个特定的范围:对于App类上的Resources字典,该范围是整个应用程序。 ContentPage类上的Resources字典适用于整个页面。 StackLayout上的Resources字典适用于StackLay中的所有子项.out。您应该根据使用方式定义和存储资源。使用App类中的Resources dictionation来获取应用程序范围的资源;使用ContentPage上的Resources字典获取页面范围的资源;但是,在可视化树中更深入地定义其他资源词典,以获取仅在页面的一个部分中所需的资源。
正如您将在第12章中看到的那样,资源字典中最重要的项通常是Style类型的对象。在一般情况下,您将拥有应用程序范围的Style对象,页面的Style对象以及与可视树的较小部分关联的Style对象。