1. 什么是资源键

资源键x:Key用作创建和引用资源的唯一标识,作用类似于名称,常出现在ResourceDictionary中且要求必须为 ResourceDictionary 内的每项定义一个键。

例:

1
2
3
4
5
6
<ResourceDictionary>
<LinearGradientBrush x:Key="fadeBrush">
<GradientStop Color="Red" Offset="0"/>
<GradientStop Color="Gray" Offset="1"/>
</LinearGradientBrush>
</ResourceDictionary>

但带有DataType的数据模板DataTemplate和带有TargetTypeStyle属于特殊情况,他们本身带有隐式键。

例:

1
2
3
4
<Style TargetType="{x:Type Button}">
<Setter Property="Background" Value="Azure">
</Setter>
</Style>

在这种情况下,该style是应用中所有 Button 控件的隐式样式。但是资源键的使用并不是这么简单,下面通过两个例子展示资源键使用过程中很可能被忽略的地方。

2. 窗体对资源字典的引用

假设有这样一个需求:给系统中的所有window设置红色背景。

能够想到的方法很多,下面列出几种。

方法1:

1.新建资源字典Dictionary1.xaml

1
2
3
4
5
6
7
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
<Style TargetType="{x:Type Window}">
<Setter Property="Background" Value="Red">
</Setter>
</Style>
</ResourceDictionary>

2.在App.xaml中添加字典引用

1
2
3
<Application.Resources>
<ResourceDictionary Source="Dictionary1.xaml" />
</Application.Resources>

方法2:

直接在App.xaml中添加样式

1
2
3
4
5
6
<Application.Resources>
<Style TargetType="{x:Type Window}">
<Setter Property="Background" Value="Red">
</Setter>
</Style>
</Application.Resources>

这两种方法都是使用了隐式的资源键,在设计器中我们也能实时的看到效果,但是当程序执行的时候其结果却和预想的不一样:

运行时窗体的背景并不是红色!

这或许是VS设计器的bug,但怎样才能实现这个需求呢?我们只需要给这个Style添加显式的资源键,并在Window中设置Style为动态资源且引用这个资源键即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
<Style x:Key="bg" TargetType="{x:Type Window}">
<Setter Property="Background" Value="Red">
</Setter>
</Style>
<Window x:Class="WpfApplication19.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="350" Style="{DynamicResource bg}" Width="525" >
<Grid/>
</Window>

这时候再运行程序,结果就正常了。

所以在WPF中对于窗体Window,不能以隐式的方式使用资源键,必须显式声明和引用。

3. 数据模板内控件样式的引用

在DataTemplate中给控件设置Style,我们直接看这个例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<Window.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Background" Value="Red"></Setter>
</Style>
<DataTemplate DataType="{x:Type local:Person}">
<TextBlock Text="{Binding Name}" />
</DataTemplate>
</Window.Resources>
<Grid>
<ItemsControl ItemsSource="{Binding Persons}"
Grid.Column="0" Margin="0,0,0,163">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel />
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
</ItemsControl>
</Grid>

在上面的例子中创建了一个DataTemplate,它包含一个TextBlock,我们想给这个TextBlock设置红色背景,所以又添加了一个有隐式键的Style。但运行结果却是无效的样式。

这是因为DataTemplate不能读取和它“平级”的Style,只能把这个Style放在它的下级DataTemplate.Resources或全局Application.Resources中。

例:

1
2
3
4
5
6
7
8
<DataTemplate DataType="{x:Type local:Person}">
<DataTemplate.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Background" Value="Red"></Setter>
</Style>
</DataTemplate.Resources>
<TextBlock Text="{Binding Name}" />
</DataTemplate>

1
2
3
4
5
<Application.Resources>
<Style TargetType="{x:Type TextBlock}">
<Setter Property="Background" Value="Red"></Setter>
</Style>
</Application.Resources>

这时候再运行程序就能看到正确的结果了: