【WPF】资源、资源字典
什么是资源
什么叫WPF的资源(Resource)?
资源是保存在可执行文件中的一种不可执行数据。在WPF的资源中,几乎可以包含图像、字符串等所有的任意CLR对象,只要对象有一个默认的构造函数和独立的属性。也就是说,应用程序中非程序代码的内容,比如点阵图、颜色、字型、动画/影片档以及字符串常量值,可将它们从程序中独立出来,单独包装成"资源(Resource)"。
资源是可以在应用程序的不同位置重用的对象。XAML资源的示例包括画笔和样式。每个框架级别的元素(FrameworkElement或FrameworkContentElement)都有一个resources属性(集合),该属性包含资源定义的资源(作为ResourceDictionary)。可以在任何元素上定义资源,但是资源通常是在根元素上定义的。当在页面根元素上定义一个资源时,页面逻辑树中的所有元素都可以访问它,并且您可以重用相同的资源来设置接受该资源表示的类型的任何属性的值。
资源分类
资源应用域
WPF提供一个封装和存取资源(resource)的机制,我们可将资源建立在应用程序的不同范围上。WPF中,资源定义的位置决定了该资源的可用范围。资源可以定义在如下范围中:
(1)物件级:此时,资源只能套用在这个Object物件,或套用至该物件的子物件。
(2)文件级:如果将资源定义在Window或Page层级的XAML档中,那么可以套用到这个文件中的所有物件。
(3)应用程序级:如果我们将资源定义在App.xaml 中,那么,就可以将资源套用到应用程序内的任何地方。
(4)字典级:当我们把资源封装成一个资源字典, 定义到一个ResourceDictionary的XAML文件时,就可以在另一个应用程序中重复使用。
每一个框架级元素( FrameworkElement 或者 FrameworkContentElement )都有一个资源属性。每一个在资源字典中的资源都有一个唯一不重复的键值(key),在标签中使用x:Key属性来标识它。一般地,键值是一个字符串,但你也可以用合适的扩展标签来设置为其他对象类型。非字符键值资源使用于特定的WPF区域,尤其是风格、组件资源,以及样式数据等。
引用方式
引用资源 静态引用资源 和动态引用资源
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:sys="clr-namespace:System;assembly=netstandard"
xmlns:conver="clr-namespace:Login.TpyeConverter" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"> <!--样式资源--> <Style x:Key="FocusVisual"> <Setter Property="Control.Template"> <Setter.Value> <ControlTemplate> <Rectangle Margin="2" StrokeDashArray="2 3" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" SnapsToDevicePixels="true" StrokeThickness="20"/> </ControlTemplate> </Setter.Value> </Setter> </Style> <!--命令资源--> <CommandBinding x:Key="Clearcommand" Command="New" Executed="CommandBinding_Executed" CanExecute="CommandBinding_CanExecute" /> <Style x:Key="OptionMarkFocusVisual"> <Setter Property="Control.Template"> <Setter.Value> <ControlTemplate> <Rectangle Margin="14,0,0,0" StrokeDashArray="3 6" Stroke="{DynamicResource {x:Static SystemColors.ControlTextBrushKey}}" SnapsToDevicePixels="true" StrokeThickness="1"/> </ControlTemplate> </Setter.Value> </Setter> </Style> <!--画刷资源--> <SolidColorBrush x:Key="OptionMark.Static.Background" Color="#FFFFFFFF"/> <!--类(字符串、数组、类型转换器等)资源--> <sys:String x:Key="balck" >黑色</sys:String>
<conver:NullableToBoolen x:Key="NullableToBoole"/>
<!--其他代码-->
<Condition Binding="{Binding ElementName=DisplayOrUnDisplay,Path=IsChecked,Converter={StaticResource NullableToBoole}}" Value="false" />
<!--其他代码-->
<!--图形资源--> <DrawingImage x:Key="DisplayPassword" > <DrawingImage.Drawing> <DrawingGroup> <GeometryDrawing Brush="#128BED" > <GeometryDrawing.Geometry> <PathGeometry Figures="M512 256c-168 0-329.6 106.4-384 256 54.4 149.6 216 256 384 256 167.2 0 330.4-106.4 384.8-256-55.2-149.6-217.6-256-384.8-256z m0 416c-88 0-160-72-160-160s72-160 160-160 160 72 160 160-72 160-160 160z m96-160c0 52.8-43.2 96-96 96s-96-43.2-96-96 43.2-96 96-96 96 43.2 96 96z"/> </GeometryDrawing.Geometry> </GeometryDrawing> </DrawingGroup> </DrawingImage.Drawing> </DrawingImage> <!--数据模板资源--> <DataTemplate DataType = "{x:Type {x:Type local:Unit}}"> <StackPanel> <TextBlock Text = "{Binding Price}"/> </StackPanel> </DataTemplate> <XmlDataProvider x:Key = "ds" XPath="Unites/Unit"> <x:XData> <Units xmlns = ""> <Unit Year = "2001" Price = "100"/> ... </Units> </x:XData> </XmlDataProvider> <!--控件模板资源--> <ControlTemplate TargetType="Button" x:Key="ButtonTemplate"> <Grid> <Ellipse Name="faceEllipse" Width="{TemplateBinding Button.Width}" Height="{TemplateBinding Control.Height}" Fill="{TemplateBinding Button.Background}"/> <TextBlock Name="txtBlock" Margin="{TemplateBinding Button.Padding}" VerticalAlignment="Center" HorizontalAlignment="Center" Text="{TemplateBinding Button.Content}" /> </Grid> <ControlTemplate.Triggers> <Trigger Property="Button.IsMouseOver" Value="True"> <Setter Property="Button.Foreground" Value="Red" /> </Trigger> </ControlTemplate.Triggers> </ControlTemplate> </ResourceDictionary>
StaticResources和Dynamic Resource使用场合
StaticResources的适用场合:
(1)在资源第一次引用之后无需再修改资源的值。
(2)资源引用不会基于运行时的行为进行重新计算,比如在重新加载Page/Window的时候。
(3)当需要设置的属性不是DependencyObject或Freezable类型的时候,用StaticResource。
(4)当需要将资源编译到dll中,并打包为程序的一部份,或者希望在各应用程序之间共享时,也使用StaticResource。
(5)当需要为一个自定义控件创建一个Theme,并Theme中使用资源,就需要使用StaticResource。因为StaticResource的资源查找行为时可预测的,并且本身包含在Theme中。而对于DynamicResource,即使资源是定义在Theme中,也只能等到运行时确定,导致一些可能意料不到的情况发生。
(6)当需要使用资源设置大量的依赖属性(Dependency Property)的时候。
(7)、引用clr中类,例如应用字符串、IValueConverter类型转换器等,IValueConverter主要用于XAML绑定和数据源之间的转换,将赋值的值转化为相应的类型。例如point=“4,5” 将4.5转化为point类型。
IValueConverters用于在数据绑定方面。通常它们在xaml文件中作为一个静态资源被创建。所以在使用时候用Converter={StaticResource ImageCoverter}}
由于依赖属性具有属性系统提供的值缓存机制,所以,如果能在程序装载时设置依赖属性的值,这样,依赖属性就不需要检查自己的值并返回最后的有效值了。
(8)、您正在使用资源来设置大量依赖项属性。依赖项属性具有由属性系统启用的有效值缓存功能,因此,如果您为可以在加载时求值的依赖项属性提供值,该依赖项属性将不必查看重新求值的表达式,并且可以返回最后一个有效值。该方法具有性能优势
(9)、引用modelview中的属性 业务界面与后端逻辑的数据绑定
它应该可以将业务界面的数据与后边逻辑的数据进行绑定,跟x:Static的功能有些类似,但感觉更强大。最近在学习MVVM,发现使用StaticResouce来绑定ViewModel层里的控件值或者Model层里的基本类型值是非常好用的*(这里有个疑问,因为我学习的时候总是被告知ViewModel层里放的是有关View的东西,所以你如果有个控件正好是View层里某个ContentControl的Content的话,那么也应该放在ViewModel层里,因为这个控件是管着View的,但我总感觉这个是不是应该放在Model层里,希望有人能够告知)*,比如你在Model层里定义了一个int属性TextBoxInt,然后在View中的TextBox的Text属性想引用这一int属性值,那么只需要在XAML中使用:
// 这里的MainWindowModel指的是ViewModel层里实例化的Model实例,ViewModel层类叫MainWindowViewModel。 < Text="{StaticResource MainWindowModel.TextBoxInt}" >
这样就可以完成数值的绑定,而且还会自动把你的int型转换成string型,这点可以说是很不错了。
写到这我突然发现好像我就知道这两种用法,当然随着后边学习用法肯定更多。但说实话,要不是学习MVVM架构,我可能也不会用StaticResource用的这么频繁,也可能不会试着去理解它。况且它还是这么能提高效率。
Dynamic Resource一般使用在如下场合:
(1)资源的值依赖一些条件,而该条件直到运行时才能确定。
包括系统资源,或是用户可设置的资源。比如:可以创建引用系统属性诸如SystemColors,SystemFonts来设置值,而这些属性是动态的,它们的值又来自于运行环境和操作系统。直接设置颜色#222444这样是静态的
(2)为自定义控件引用或创建Theme Style。比如按钮Style,程序运行过程中能切换不同的Style。
(3)希望在程序运行期间调整资源字典的内容时。
(4)希望资源可以向前引用时(如上面在Canvas中引用innerLgbResource一样)
(5)资源文件很大,希望在运行时才加载。
(6)要创建的Style的值可能来自于其它值,而这些值又依赖于Theme或用户的设置。
(7)当引用资源的元素的父元素有可能在运行期改变,这个时候也需要使用动态资源。因为父元素的改变将导致资源查询的范围。例如 imgae就是动态资源
Dynamic resource的限制条件:属性必须是依赖属性,或是Freezable的。
资源的查询方式
Static Resource的查询
(1)查找使用该资源的元素的Resource字典;
(2)顺着逻辑树向上查找父元素的资源字典,直到根节点;
(3)查找Application资源;
(4)不支持向后引用,即:不能引用在引用点之后才定义的资源。
Dynamic Resource的查询
(1)查找使用该资源的元素的Resource字典;
如果元素定义了一个Style 属性,将查找Style中的资源字典;如果元素定义了一个Template属性,将查找FrameworkTemplate中的资源字典。
(2)顺逻辑树向上查找父元素的资源字典,直到根节点;
(3)查找Application资源;
(4)查找当前激活状态下的Theme资源字典;
(5)查找系统资源。
引用资源
在C#代码中引用资源
资源不仅可以在XAML代码中访问,也可以使用C#代码访问和控制它们。方法是使用FindResource查找资源,Resource.Add增加资源和Resource.Remove(移除资源)。
xaml中引用资源
静态的引用资源
StaticResource 查询行为不支持向前引用,即不能引用在引用点之后才定义的资源。而DynamicResource可以向前引用,即DynamicResource运行时才查找并加载所定义的资源。
注意:
Windows8应用中,XAML资源仅支持StaticResource(静态资源);
资源应用域不同,XAML资源可分为FrameworkElement.Resources和 Application.Resources
FrameworkElement.Resources是将资源对象应用于同一个对象数的不同对象上,称之为页面资源,通常被定义在XAML页面根元素上。
Application.Resources是贯穿整个应用级别的资源,通常被定义在App.xaml页面
FrameworkElement.Resources(页面资源):前面已经举例,这里就不详述。
Application.Resourcess(贯穿整个应用级别的资源)
静态资源字典引用方式如下:
<TextBlock Text="{StaticResource ResourceKey=balck}" />
或者
//ResourceKey 是内容属性,可以省略不写
<TextBlock Text="{StaticResource balck}" />
动态的引用资源
DynamicResource将属性值延迟为资源的运行时引用,从而为属性提供一个值。动态资源引用在每次访问此类资源并在运行时访问对象图时强制执行新的查找。为了获得这种访问,WPF属性系统中的依赖属性支持DynamicResource概念,并计算表达式。因此,只能对依赖项属性目标使用DynamicResource。
使用ComponentResourceKey时 ,必须使用 DynamicResource ,例如在程序集间使用资源字典共享资源时候,就必须使用DynamicResource
资源 的定义位置
控件查找资源的顺序 1》2》3》4
在基本控件中都有resources这个属性,该属性是个集合类型,支持索引,而且查询资源的时候,会先从本控件开始找,然后一层层向上,最后会到达application的资源,
1、在控件元素中定义资源
<Window x:Class="DP.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:DP" xmlns:sys="clr-namespace:System;assembly=netstandard" xmlns:hc="https://handyorg.github.io/handycontrol" mc:Ignorable="d" Name="ThisWin" Title="MainWindow" Height="450" Width="800"> <Grid> <Label Content="{DynamicResource NBam}"> <Label.Resources> <sys:String x:Key="NBam" >颜色</sys:String> </Label.Resources> </Label>
2、在窗体定义资源
窗体页面的根。可以在任何元素上定义资源,但是资源通常是在根元素上定义的。当在页面根元素上定义一个资源时,页面逻辑树中的所有元素都可以访问它,并且您可以重用相同的资源来设置接受该资源表示的类型的任何属性的值。
<Window x:Class="DP.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:DP" xmlns:sys="clr-namespace:System;assembly=netstandard" xmlns:hc="https://handyorg.github.io/handycontrol" mc:Ignorable="d" Name="ThisWin" Title="MainWindow" Height="450" Width="800"> <Window.Resources> <sys:String x:Key="NBam" >颜色</sys:String>
<CommandBinding x:Key="Clearcommand" Command="local:CustomCommands.Clear" Executed="CommandBinding_Executed" CanExecute="CommandBinding_CanExecute" />
</Window.Resources> <Grid> <Label Content="{StaticResource NBam}"></Label> </Grid> </Window>
3、在资源字典中定义资源Application --App.xml,代码如下:
资源字典的作用
主要原因有两个:
- 提供皮肤功能。
- 存储需要被本地话的内容(错误消息字符串等,实现软编码)
<Application x:Class="DP.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:DP" StartupUri="MainWindow.xaml"> <Application.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <!--将以下的资源字典 归并道app.xaml 字典中一起编译成baml--> <!--务必将app.xaml和以下的资源字典都设置成生成操作 bulid action--> <ResourceDictionary Source="Dirctionary/colour.xaml"/> <ResourceDictionary Source="Dirctionary/Skin.xaml"/> </ResourceDictionary.MergedDictionaries> <local:ChineseNumToNumberConvert x:Key="ChineseNumToNumberConvertKey"/> </ResourceDictionary> </Application.Resources> </Application>
colour.xaml 代码如下
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=netstandard" > <sys:String x:Key="balck" >黑色</sys:String> <sys:String x:Key="wihte" >白色</sys:String> <sys:String x:Key="blue" >蓝色</sys:String> </ResourceDictionary>
Skin.xaml代码如下
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:sys="clr-namespace:System;assembly=netstandard" > <sys:String x:Key="autumn" >秋天</sys:String> <sys:String x:Key="spring" >春天</sys:String> <sys:String x:Key="winter" >冬天</sys:String> </ResourceDictionary>
应用 代码如下:
<Window x:Class="DP.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:DP" xmlns:sys="clr-namespace:System;assembly=netstandard" xmlns:hc="https://handyorg.github.io/handycontrol" mc:Ignorable="d" Name="ThisWin" Title="MainWindow" Height="450" Width="800"> <Grid> <Grid.RowDefinitions> <RowDefinition Height="60*"></RowDefinition> <RowDefinition Height="60*"></RowDefinition> </Grid.RowDefinitions> <Grid.ColumnDefinitions> <ColumnDefinition/> <ColumnDefinition/> </Grid.ColumnDefinitions> <StackPanel Orientation="Vertical" Grid.Row="0"> <Label>中文数字</Label> <TextBox x:Name="wc1">一</TextBox> <TextBox x:Name="wc2">二</TextBox> <TextBox x:Name="wc3">三</TextBox> </StackPanel> <StackPanel Orientation="Vertical" Grid.Row="0" Grid.Column="1"> <Label>英文数字</Label> <TextBox Text="{Binding ElementName=wc1,Path=Text, Converter={StaticResource ResourceKey=ChineseNumToNumberConvertKey}}"></TextBox> <TextBox Text="{Binding ElementName=wc2,Path=Text, Converter={StaticResource ResourceKey=ChineseNumToNumberConvertKey}}"></TextBox> <TextBox Text="{Binding ElementName=wc3,Path=Text, Converter={StaticResource ResourceKey=ChineseNumToNumberConvertKey}}"></TextBox> </StackPanel> <StackPanel Orientation="Vertical" Grid.Row="1" Grid.Column="0"> <Label>颜色</Label> <TextBlock Text="{StaticResource ResourceKey=balck}" /> <TextBlock Text="{StaticResource ResourceKey=wihte}" /> <TextBlock Text="{StaticResource ResourceKey=blue}" /> </StackPanel> </Grid> </Window>
4、框架中的资源
框架dll文件中的资源(意味可以自定义 dll文件,并且引用此文件)
主要存在system命名空间中, SystemFonts 字体类、SystemColors 颜色类、SystemParameters 参数类(键盘 、屏幕、鼠标大小等设置、以及图形的效果(阴影 拖动等))。
5、程序集间使用资源字典共享资源
(1)创建
首先是共享资源的创建,创建一个新的“wpf自定义控件”项目,然后随便添加一个图片作为示例资源。
文件结构如下:
资源字典文件(background.xaml)里面的代码如下:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:ResourceLiarary" > <ImageBrush x:Key="{ComponentResourceKey TypeInTargetAssembly={x:Type local:CustomControl1},ResourceId=test}" ImageSource="pack://application:,,,/ResourceLiarary;component/Snipaste.png"></ImageBrush> </ResourceDictionary>
因为这个资源是要作为共享资源的,所以这里要使用绝对 pack URI路径
(2)资源配置
1.资源合并
如果你创建了多个资源字典作为共享资源的话,进行资源合并就是必须的,资源合并的代码写在Generic.xaml里:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:local="clr-namespace:ResourceLiarary" > <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="pack://application:,,,/ResourceLiarary;component/background.xaml"/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary>
这里的重点也和上面一样,要使用绝对url路径
2.类文件
这一步其实可以是可选的,但加上之后可以简化资源的使用,因此也是用必要的
public class CustomControl1 { public static ComponentResourceKey test { get { return new ComponentResourceKey(typeof(CustomControl1), "test"); } } }
3、将图片设置为“资源” ,就会将图片嵌入程序集中,方便引用。
(3)、编译生成 组件“ResourceLiarary.dll”
(4)、在新项目中引入该com组件“ResourceLiarary.dll”
(5) xaml 命名空间引入ResourceLiarary
(6)xaml 代码引入ResourceLiarary 命名空间
<Button Background="{DynamicResource {x:Static res:CustomControl1.test}}" >
<Window x:Class="DP.MainWindow" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:local="clr-namespace:DP" xmlns:sys="clr-namespace:System;assembly=netstandard" xmlns:hc="https://handyorg.github.io/handycontrol" xmlns:res="clr-namespace:ResourceLiarary;assembly=ResourceLiarary" mc:Ignorable="d" Name="ThisWin" Title="MainWindow" Height="450" Width="800"> <Grid> <Button Background="{DynamicResource {x:Static res:CustomControl1.test}}" > </Button> </Grid> </Window>
res:CustomControl1.test 是ComponentResourceKey 所以必须使用 DynamicResource