你最希望声明为 XAML 资源的 XAML 元素包括 Style、ControlTemplate、动画组件和 Brush 子类。 我们在此处介绍
如何定义 ResourceDictionary 和键控资源
XAML 资源与你定义为应用或应用包一部分的其他资源有何关系
- 资源字典高级功能
- MergedDictionaries
- ThemeDictionaries。
1. 定义和使用 XAML 资源
<Page
x:Class="SpiderMSDN.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<SolidColorBrush x:Key="myFavoriteColor" Color="green"/>
<x:String x:Key="greeting">Hello world</x:String>
</Page.Resources>
<TextBlock Foreground="{StaticResource myFavoriteColor}" Text="{StaticResource greeting}" VerticalAlignment="Top"/>
<Button Foreground="{StaticResource myFavoriteColor}" Content="{StaticResource greeting}" VerticalAlignment="Center"/>
</Page>
所有资源都需要有一个键。 通常,键是通过 x:Key="myString"
定义的字符串。 但是,还有几种其他方法可指定键:
如果未指定 x:Key,则 Style 和 ControlTemplate 需要 TargetType,并且将 TargetType 用作键。 在这种情况下,键是实际的 Type 对象,而非字符串。
如果未指定 x:Key,具有 TargetType 的 DataTemplate 资源会将 TargetType 用作键。 在这种情况下,键是实际的 Type 对象,而非字符串。
- x:Name 可以代替 x:Key 使用。 但是,x:Name 还会为资源生成代码隐藏字段。 因此,x:Name 的效率低于 x:Key,因为该字段需要在页面加载时进行初始化
StaticResource 标记扩展可以仅使用字符串名称(x:Key 或 x:Name)检索资源。 但是,XAML 框架在决定为尚未设置 Style 和 ContentTemplate 或 ItemTemplate 属性的控件使用哪个样式和模板时,还将查找隐式样式资源(使用 TargetType 而非 x:Key 或 x:Name 的资源)。
此处的 Style 具有隐式键 typeof(Button)
,并且由于页面底部的 Button 未指定 Style 属性,它将查找具有 typeof(Button) 键的样式:
<Page
x:Class="MSDNSample.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<Style TargetType="Button">
<Setter Property="Background" Value="red"/>
</Style>
</Page.Resources>
<!-- This button will have a red background. -->
<Button Content="Button" Height="100" VerticalAlignment="Center" Width="100"/>
</Page>
2. 查找代码中的资源
像任何其他字典一样,访问资源字典的成员
注意 当你执行在代码中查找资源的操作时,仅找到 Page.Resources 字典中的资源。 与 StaticResource 标记扩展不同,如果未在第一个字典中找到这些资源,该代码不会回退到 Application.Resources 字典。
此示例演示如何在页面的资源字典中检索出 redButtonStyle 资源
<Page
x:Class="MSDNSample.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<Style TargetType="Button" x:Key="redButtonStyle">
<Setter Property="Background" Value="red"/>
</Style>
</Page.Resources>
</Page>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
Style redButtonStyle = (Style)this.Resources["redButtonStyle"];
}
}
若要在代码中查找应用范围的资源,请使用 Application.Current.Resources 获取应用的资源目录,如下所示
<Application
x:Class="MSDNSample.App"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:SpiderMSDN">
<Application.Resources>
<Style TargetType="Button" x:Key="appButtonStyle">
<Setter Property="Background" Value="red"/>
</Style>
</Application.Resources>
</Application>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
Style appButtonStyle = (Style)Application.Current.Resources["appButtonStyle"];
}
}
以下是在执行此操作时要记住的两个事项:
- 第一,在任何页面尝试使用资源时,你需要先添加资源。
- 第二,不能在应用的构造函数中添加资源
如果在 Application.OnLaunched 方法中添加资源,可以避免这两个问题,如下所示
// App.xaml.cs
sealed partial class App : Application
{
protected override void OnLaunched(LaunchActivatedEventArgs e)
{
Frame rootFrame = Window.Current.Content as Frame;
if (rootFrame == null)
{
SolidColorBrush brush = new SolidColorBrush(Windows.UI.Color.FromArgb(255, 0, 255, 0)); // green
this.Resources["brush"] = brush;
// … Other code that VS generates for you …
}
}
}
3.每个 FrameworkElement 都可以具有 ResourceDictionary
FrameworkElement 是控件所继承的基类,并且具有 Resources 属性。 因此你可以将本地资源字典添加到任何 FrameworkElement
此时,资源字典已添加到页面元素
<Page
x:Class="MSDNSample.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<x:String x:Key="greeting">Hello world</x:String>
</Page.Resources>
<Border>
<Border.Resources>
<x:String x:Key="greeting">Hola mundo</x:String>
</Border.Resources>
<TextBlock Text="{StaticResource greeting}" Foreground="Gray" VerticalAlignment="Center"/>
</Border>
</Page>
此时,Page 和 Border 都具有资源字典,并且都具有名为“greeting”的资源。 TextBlock 位于 Border 内,因此其资源查找将依次查找 Border 的资源、Page 的资源以及 Application 资源。 TextBlock 将读取“Hola mundo”
若要从代码访问元素的资源,请使用该元素的 Resources 属性。 在代码(而非 XAML)中访问 FrameworkElement 的资源,将仅在该字典中查找,而不在父级元素的字典中查找
<Page
x:Class="MSDNSample.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<x:String x:Key="greeting">Hello world</x:String>
</Page.Resources>
<Border x:Name="border">
<Border.Resources>
<x:String x:Key="greeting">Hola mundo</x:String>
</Border.Resources>
</Border>
</Page>
public sealed partial class MainPage : Page
{
public MainPage()
{
this.InitializeComponent();
string str = (string)border.Resources["greeting"];
}
}
4. 合并的资源字典(上一篇也有,简化版)
合并的资源字典将一个资源字典合并到通常在其他文件中的另一个字典
提示 可以在 Microsoft Visual Studio 中创建资源字典文件,方法是在项目菜单中依次使用add > new item…> dictionary recourse
此时,可以在名为 Dictionary1.xaml 的单独 XAML 文件中定义资源字典
<!-- Dictionary1.xaml -->
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MSDNSample">
<SolidColorBrush x:Key="brush" Color="Red"/>
</ResourceDictionary>
若要使用该字典,请将它与页面的字典合并:
<Page
x:Class="MSDNSample.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<ResourceDictionary>
<ResourceDictionary.MergedDictionaries>
<ResourceDictionary Source="Dictionary1.xaml"/>
</ResourceDictionary.MergedDictionaries>
<x:String x:Key="greeting">Hello world</x:String>
</ResourceDictionary>
</Page.Resources>
<TextBlock Foreground="{StaticResource brush}" Text="{StaticResource greeting}" VerticalAlignment="Center"/>
</Page>
下面是本示例中会出现的结果。 在 <Page.Resources>
中,声明 <ResourceDictionary>
。 当你向 <Page.Resources>
添加资源时,XAML 框架将为你隐式创建资源字典;但是在这种情况下,你需要的不仅是任一资源字典,你还需要包含合并字典的资源字典。
因此请声明 <ResourceDictionary>
,然后将内容添加到其 <ResourceDictionary.MergedDictionaries>
集合。 其中每个条目都采用 <ResourceDictionary Source="Dictionary1.xaml"/>
形式。 若要添加多个字典,只需在第一个条目后添加 <ResourceDictionary Source="Dictionary2.xaml"/>
条目即可
在 <ResourceDictionary.MergedDictionaries>…</ResourceDictionary.MergedDictionaries>
后,你可以选择在主字典中放置其他资源。 你可以使用要合并到的字典中的资源,如同使用常规字典一样。 在上述示例中,{StaticResource brush}
在子级/合并字典 (Dictionary1.xaml)
中查找资源,而 {StaticResource greeting}
在主页字典中查找其资源
在资源查找序列中,仅在检查 ResourceDictionary 的所有其他键控资源后,才会检查 MergedDictionaries 字典。 搜索该级别后,查找会深入到合并后的字典,且对 MergedDictionaries 中的每一项进行检查。 如果存在多个合并的字典,会按在 MergedDictionaries 属性中声明这些字典的顺序的相反顺序来检查它们。 在以下示例中,如果 Dictionary2.xaml 和 Dictionary1.xaml 声明同一个键,则首先使用来自 Dictionary2.xaml 中的键,因为它排在 MergedDictionaries 集的末尾
在任一 ResourceDictionary 范围内,均会检查字典中键的唯一性。 但是,这一范围不会扩展到不同 MergedDictionaries 文件中的不同项
可以结合使用查找序列和跨合并字典范围不强制使用唯一键来创建 ResourceDictionary 资源的回退值序列。 例如,你可能会使用与应用的状态数据和用户首选项数据同步的资源词典,为序列中最后合并的资源字典中的特殊画笔颜色存储用户首选项。 但是,如果尚不存在任何用户首选项,则可为初始 MergedDictionaries 文件中的 ResourceDictionary 资源定义相同的键字符串,并可将其用作回退值。
请记住,始终会在检查合并字典之前检查你在主要资源字典中提供的任何值,所以如果希望使用回退技术,则不要在主要资源字典中定义该资源
5.主题资源和主题字典
ThemeResource 类似于 StaticResource,但资源查找会在主题更改时进行重新评估。
在此示例中,将 TextBlock 的前景设置为当前主题中的值
<TextBlock Text="hello world" Foreground="{ThemeResource FocusVisualWhiteStrokeThemeBrush}" VerticalAlignment="Center"/>
主题字典是一种特殊类型的合并字典,用于保存各种资源,具体资源取决于用户当前在其设备上使用的主题。 例如,“浅色”主题可能使用白色画笔,而“深色”主题可能使用黑色画笔。 画笔会更改它所溶入的资源,但使用该画笔作为资源的控件的组成可能保持不变。 若要在个人模板或样式中重现主题切换行为而不将 MergedDictionaries 用作属性以将项目合并到主词典中,请使用 ThemeDictionaries 属性
ThemeDictionaries 内的每个 ResourceDictionary 元素必须具有一个 x:Key 值。 该值是一个字符串,它为相关主题命名,例如 “Default”、“Dark”、“Light”或“HighContrast”。 通常,Dictionary1 和 Dictionary2 将定义名称相同但值不同的资源。
在此处,将红色文本用于浅色主题,蓝色文本用于深色主题
<!-- Dictionary1.xaml -->
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MSDNSample">
<SolidColorBrush x:Key="brush" Color="Red"/>
</ResourceDictionary>
<!—Dictionary2.xaml -->
<ResourceDictionary
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="using:MSDNSample">
<SolidColorBrush x:Key="brush" Color="blue"/>
</ResourceDictionary>
在此示例中,将 TextBlock 的前景设置为当前主题中的值
<Page
x:Class="MSDNSample.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Page.Resources>
<ResourceDictionary>
<ResourceDictionary.ThemeDictionaries>
<ResourceDictionary Source="Dictionary1.xaml" x:Key="Light"/>
<ResourceDictionary Source="Dictionary2.xaml" x:Key="Dark"/>
</ResourceDictionary.ThemeDictionaries>
</ResourceDictionary>
</Page.Resources>
<TextBlock Foreground="{StaticResource brush}" Text="hello world" VerticalAlignment="Center"/>
</Page>
对于主题字典,每当使用 ThemeResource 标记扩展进行引用并且系统检测到主题更改时,要用于资源查找的活动字典都会动态更改。 系统执行的查找行为基于将活动主题映射到特定主题字典的 x:Key 操作
检查主题字典在默认 XAML 设计资源中的构建方式十分有用,这些资源与 Windows 运行时默认用作其控件的模板相对应。 使用文本编辑器或 IDE 打开 \(Program Files)\Windows Kits\10\DesignTime\CommonConfiguration\Neutral\UAP\<SDK version>\Generic 中
的 XAML 文件。 请注意主题字典首先在 generic.xaml 中的定义方式,以及每个主题字典定义相同键的方式。 然后每个这样的键由构成各种键控元素的元素所引用,这些键控元素位于主题字典外部并且稍后在 XAML 中定义。 还存在用于设计的单独 themeresources.xaml 文件,该文件仅包含主题资源和额外模板,不包含默认控件模板。 这些主题区域是你将在 generic.xaml 中看到的内容副本。
当你使用 XAML 设计工具以编辑样式和模板的副本时,设计工具会提取 XAML 设计资源词典中的片段并将其作为应用和项目一部分的 XAML 字典元素的本地副本放置。
有关可用于应用的特定于主题的资源和系统资源的详细信息和列表,请参阅 XAML 主题资源