WPF资源
分类:
IT文章
•
2023-11-04 10:34:01
WPF资源系统是一种保管一系列有用的对象的简单方法,从而可以更容易的重用这些对象。尽管可以在代码中创建和操作资源,当通常在XAML标记中定义资源。一旦定义了一个资源,就可以在窗口中标记的所有其他部分使用该资源。这种技术简化了标记,保存了重复的编码,并且可以在*位置存储用户界面的细节,以便方便的修改它们。
10.1 资源基础
WPF允许在代码中以及在标记中的各个位置定义资源。资源具有许多重要的优点:
- 高效。通过资源可以定义一个对象,并在标记中的多个地方使用。这会使代码变得更加精简,并且更加高效
- 可维护性。通过资源可以使用低级的格式化细节,并将他们移到方便对其进行修改的*位置。在XAML中创建资源,相当于在代码中创建常量
- 适应性。一旦特定的信息和应用程序的其他部分相分离,并且放置到资源部分中,就可以动态地修改它。
10.1.1 资源集合
每个元素都有一个Resources属性,该属性存储了一个资源字典集合(它是Resource Dictionary类的实例)。资源集合可以包含任意类型的对象,并根据字符串编写索引。尽管每个元素都提供了Resources属性(该属性是作为FrameworkElement类的一部分定义的),但是通常在窗口级别上定义资源。这是因为每个元素都可以访问他自己的资源集合中的资源,也可以访问所有父元素的资源集合中的资源。如下示例中:
<Window x:Class="Resources.WindowResource"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Resources" Height="300" Width="300"
>
<Window.Resources>
<ImageBrush x:Key="TileBrush" TileMode="Tile"
ViewportUnits="Absolute" Viewport="0 0 32 32"
ImageSource="happyface.jpg" Opacity="0.3"></ImageBrush>
</Window.Resources>
<StackPanel Margin="5">
<Button Background="{StaticResource TileBrush}" Padding="5"
FontWeight="Bold" FontSize="14" Margin="5"
>A Tiled Button</Button>
<Button Padding="5" Margin="5"
FontWeight="Bold" FontSize="14">A Normal Button</Button>
<Button Background="{StaticResource TileBrush}" Padding="5" Margin="5"
FontWeight="Bold" FontSize="14"
>Another Tiled Button</Button>
</StackPanel>
</Window>
View Code
把一个画刷定义为资源
<Window.Resources>
<ImageBrush x:Key="TileBrush" TileMode="Tile"
ViewportUnits="Absolute" Viewport="0 0 32 32"
ImageSource="happyface.jpg" Opacity="0.3"></ImageBrush>
</Window.Resources>
,重要的是第一个特性,即Key特性(以名称空间前缀x:开头,这会将该画刷放置到XAML名称空间中,而不是WPF名称空间中)。该特性指定了在Window.Resources.Collection集合中编写画刷索引的名称。为了使用XAML标记中的资源,需要一种引用资源的方法。这是通过标记扩展完成的。有两种标记扩展的方法,一种是静态资源,另一种是动态资源。静态资源在第一次创建窗口时一次性的设置完毕。而对于动态资源,如果发生了变化,则会重新应用资源。下面分别是静态和动态资源的引用:
//静态资源
<Button Background="{StaticResource TileBrush}" Padding="5"
FontWeight="Bold" FontSize="14" Margin="5"
>A Tiled Button</Button>
//动态资源
<Button Background="{DynamicResource TileBrush}" Padding="5"
FontWeight="Bold" FontSize="14" Margin="5"
>A Tiled Button</Button>
10.1.2 资源的层次
每个元素都有自己的资源集合,并且为了找到期望的资源,WPF在元素树中进行递归搜索。当使用静态资源时,必须在应用资源之前在标记中定义资源。如果希望在按钮元素中放置资源,需要稍微重新安排标记,在设置背景之前定义资源,如下:
<Button Padding="5" Margin="5"
FontWeight="Bold" FontSize="14">
<Button.Resources>
<ImageBrush x:Key="TileBrush" TileMode="Tile"
ViewportUnits="Absolute" Viewport="0 0 10 10"
ImageSource="happyface.jpg" Opacity="0.3"></ImageBrush>
</Button.Resources>
<Button.Background><StaticResource ResourceKey="TileBrush" /></Button.Background>
<Button.Content>Another Tiled Button</Button.Content>
</Button>
View Code
因为资源被放在一个嵌套的元素中,为了指向正确的资源,资源键是使用ResourceKey属性具*定的。只要在不同一集合中多次使用相同的资源名,就可以重用相同的资源名称。
10.1.3 静态资源和动态资源
静态资源和动态资源的区别在于静态资源只从资源集合中获取对象一次。根据对象的类型,对象的任何变化都可能被立即注意到。然而,动态资源在每次需要对象时都会重新从资源集合中查找对象。这意味着可以在同一键下放置一个全新的对象,并且动态资源会应用该变化。作为一般规则,只有在下列情况下才需要使用动态属性:
- 资源具有依赖与系统设置的属性(如当前Windows操作系统的颜色或字体)
- 计划通过编程替换资源对象
但是,不应过度使用动态资源。主要问题是对资源的修改未必会触发对用户界面的更新。在许多情况下,需要在控件中显示动态内容,并且控件随着内容的改变需要调整自身,对于这种情况,使用数据绑定更合理。
10.1.4 非共享资源
通常,当在多个地方使用某种资源时,使用的是同一个对象实例。这种行为——被称为共享。然而,也可能希望告诉解析器在每次使用时创建单独的对象实例。为了关闭共享行为,需要使用Shared特性,如下所示:
<ImageBrush x:key="TileBrush" x:Shared="False">...</ImageBrush>
10.1.5 通过代码访问资源
我们一般在标记中定义和使用资源。如果需要,我们也可以在代码中使用资源集合。可以从资源中提取资源。为此,需要使用正确元素的资源集合。可以使用FrameworkElement.FindResource()方法以相同的方式查找资源。可以使用TryFindResource()方法替代FindResource()方法。如果找不到资源,该方法会返回一个空引用,而不是抛出一个异常。此外,还可以通过编写代码添加资源。选择希望放置资源的元素,并使用资源集合的Add()方法。
10.1.6 应用程序资源
窗口不是查找资源的最后一站。如果在控件中或其他容器中找不到指定的资源,WPF会继续检查为应用程序定义的资源集合。在Visual Studio中,这些资源是在App.xaml文件的标记中定义的资源,如下所示:
<Window x:Class="Resources.WindowResource"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Resources" Height="300" Width="300"
>
<Application.Resources>
<ImageBrush x:Key="TileBrush" TileMode="Tile"
ViewportUnits="Absolute" Viewport="0 0 32 32"
ImageSource="happyface.jpg" Opacity="0.3"></ImageBrush>
</Application.Resources>
</Application>
应用程序资源位在整个应用程序中重用对象提供给了一种很好的方法。当某个查找资源时,应用程序资源仍然不是最后一站。如果在应用程序资源中没有找到所需要的资源,元素还会继续查找系统资源。
10.1.7 系统资源
动态资源主要用于辅助应用程序对系统环境设置的变化做出响应。但这会导致一个问题,如何检索系统环境设置并在代码中使用它们?为此需要使用三个类,分别是SystemColors、SystemFonts和SystemParameters,所有这些类都位于System.Windows名称空间中。SystemColors类用于访问颜色设置;SystemFonts类用于访问字体设置;而SystemParameters类包装了大量的设置列表,这些设置列表描述了各种屏幕元素的标准尺寸、键盘和鼠标设置、屏幕尺寸以及各种图形效果是否已经打开。SystemColors、SystemFonts和SystemParameters类通过静态属性提供了他们所有的细节。例如,SystemColors.WindowTextColor属性提供一个Color结构,可以方便的使用该结构。下面的示例使用该属性创建一个画刷,并填充元素的前景:
label.Foreground=new SolidBrush(SystemColors.WindowTextColor);
或者为了提高效率,可以使用现成的画刷属性:
label.Foreground=SystemColors.WindowTextBrush;
在WPF中,可以使用静态标记扩展访问静态属性。例如,下面的标记演示了如何使用XAML为同一标签设置前景色:
<Label Foreground="{x:Static SystemColors.WindowTextBrush}">Ordinary text</Label>
上面的示例没有使用资源,当解析窗口并且创建标签时,会根据当前窗口文本颜色的“快照”创建画刷。如果在应用程序运行时改变了Windows颜色,Label控件不会更新它自身。为此,不能将Foreground属性直接设置为画刷对象。而是需要将它设置为包装了该系统资源的DynamicResource对象。幸运的是,所有SystemXxx类都提供了用于返回ResourceKey对象引用的补充属性集,使用这些引用可以从系统资源集合中提取资源。这些属性与直接返回对象的破同属性同名,后面加上单词Key。下面是标记显示了如何使用来自SystemXxx类的资源:
<Label Foreground="{DynamicResource {x:Static SystemColors.WindowTextBrushKey}}">Ordinary text</Label>
上面示例中首先定义了一个动态资源,但该动态资源不是从应用程序的资源集合中提取资源。而是使用了一个由SystemColors.WindowTextBrushKey属性定义的键。因为该属性是静态属性,所以还需要使用一个静态标记扩展,从而让解析器理解正在尝试进行什么操作。
10.2 资源字典
如果希望在多个项目之间共享资源,可以创建一个资源字典。资源字典是一个简单的XAML文档,该文档除了存储希望使用的资源之外,不做其他任何事情。
10.2.1 创建资源字典
下面是一个资源字典示例,它包含一个资源:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:ResourceLibrary"
>
<ImageBrush
x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:CustomResources}, ResourceId=SadTileBrush}"
TileMode="Tile"
ViewportUnits="Absolute" Viewport="0 0 32 32"
ImageSource="ResourceLibrary;component/sadface.jpg" Opacity="0.3">
</ImageBrush>
</ResourceDictionary>
View Code