原文WPF学习之资源-Resourceshtml
WPF经过资源来保存一些能够被重复利用的样式,对象定义以及一些传统的资源如二进制数据,图片等等,而在其支持上也更能体现出这些资源定义的优越性。好比经过ResourceDictionary的支持就能够经过资源来实现换肤功能,在ExpressionBlend中设计的酷炫造型也能够经过导出成资源来很容易的被程序员所引用,本地化的实现,访问另外程序集的嵌入式资源等等。这些都给咱们提供了丰富的手段经过资源访问架构来构建丰富的富媒体应用程序。本文简单讲解了WPF Resources的分类及其常见用法,并简单触及用ResourceDictionary来管理多个Resources文件(这是换肤的基础)。程序员
在WPF中的资源不只依赖于核心.NET的资源系统,在其基础上也添加了对两种不一样资源类型的支持:二进制资源和逻辑资源。而对于这些资源类型的构建动做也有了更多的支持选项。web
1. 二进制资源windows
二进制资源实际上是一些传统的资源项,好比位图,音频文件,视频文件,松散文件(Loose file)等等。对于这些资源项咱们能够将其存储为松散文件,或者编译进程序集中。这与传统的.NET程序实际上是相通的,但在WPF中提供了两种对二进制资源的构建选项:架构
· Resource: 将资源放入程序集中(若是是有本地化支持的话会编译到对应语言集的子程序集中。ide
· Content:将这个资源做为一个松散文件加入到程序集中,程序集会记录对应的文件是否存在及其路径。这就至关于咱们web开发中经常使用的构建动做。函数
对于MSBuild来讲这也是默认的构建类型,例如,post
<Content Include="Images\Go.ico" />性能 <Content Include="Images\Go.jpg" />学习 <Content Include="Images\Go2.gif" /> <Content Include="Images\Go2.jpg" /> <Content Include="Images\information16.png" /> <Content Include="Images\pass16.png" /> <Content Include="Images\pass32.png" /> <Content Include="Images\unknown16.png" /> <Content Include="Images\warning.gif" /> <Content Include="Images\warning16.png" /> <EmbeddedResource Include="LoginForm.resx"> <SubType>Designer</SubType> <DependentUpon>LoginForm.cs</DependentUpon> </EmbeddedResource> <EmbeddedResource Include="OptionsForm.resx"> <SubType>Designer</SubType> <DependentUpon>OptionsForm.cs</DependentUpon> </EmbeddedResource> <EmbeddedResource Include="PageHistory.resx"> <SubType>Designer</SubType> <DependentUpon>PageHistory.cs</DependentUpon> </EmbeddedResource> |
上图所示是一个普通的WinForm应用程序的项目文件,对于添加到其内部的二进制资源文件其默认的构建动做即是Content-代表其做为一个松散文件存储,只要保证其对应路径的文件存在则能够自动加载(而无需再你的打包文件中必须包含)。而你也会看到EmbeddedResource构建动做,这是WinForm的构建动做,它和Resource构建动做很类似,会在程序集中嵌入一个二进制资源,可是WPF中由于嵌入式资源比WPF还要优先,因此须要尽可能避免使用。
之因此推荐使用Resource和Content构建类型是由于这样嵌入的资源能够很容易的在XAML中被引用,并且对于WPF的统一资源识别符也是专门针对这两种构建动做而设计的。相反地,对于EmbeddedResource构建动做嵌入的资源是不能在XAML中被引用的,除非自定义代码。
· 访问二进制资源
访问二进制资源最普通的就是对松散文件的访问,这和普通的.NET应用程序没什么两样,直接看例子吧:
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <Button Background="Yellow" BorderBrush="Red" Margin="5"> <Image Height="21" Source="zoom.gif"/> </Button> <Button Background="Yellow" BorderBrush="Red" Margin="5"> <Image Height="21" Source="defaultThumbnailSize.gif"/> </Button> <Button Background="Yellow" BorderBrush="Red" Margin="5"> <Image Height="21" Source="previous.gif"/> </Button> </StackPanel> |
上述的的松散文件只须要以Content构建加入项目便可. 这只是普通的访问方式,固然也是咱们经常使用的,但也有不少你可能须要用到哦,不妨看看:
资源URI |
资源 |
zoom.gif |
存放于当前程序集;或是添加到项目的松散文件。 |
Folder/zoom.gif |
若是内部有目录结构时加入相对目录结构。 |
C:\Images\zoom.gif |
绝对路径的松散文件 |
\\FileServer\Images\zoom.gif |
共享目录的松散文件 |
http://my.net/zoom.gif |
位于某个站点上的松散文件 |
AssemblyReference;Component/ResourceName |
访问嵌入到另一个程序集或EXE文件内的资源。Component是关键字,必须写。例如: MyDll;Component/Images/zoom.gif; |
pack://siteOfOrigin:,,,/Images/zoom.gif |
访问位于部署位置的资源。 |
2. 逻辑资源
逻辑资源是WPF特有的资源类型,它是存储在元素的Resources属性中的.NET对象,一般须要共享给多个子元素。换句话说,你能够声明一个SolidColorBrush对象看成一个逻辑资源,你也能够声明一个Style,而后再后续的XAML中简单经过{StaticResource ResourceName}来使用。资源定义须要有一个在ResourceDictionary中惟一的关键字x:Key(单独的ResourceDictionary中的键名不能够重复,多个ResourceDictionary中键名能够重复,会根据在逻辑数上的lookup的顺序来就近生效)。例以下边的逻辑资源声明:
<Window x:Class="WpfApplication1.Window2" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window2" Height="300" Width="400"> <Window.Resources> <SolidColorBrush x:Key="buttonBackground">Yellow</SolidColorBrush> <SolidColorBrush x:Key="borderBrush">Red</SolidColorBrush> <LinearGradientBrush x:Key="backgroundBrush" StartPoint="0,0" EndPoint="1,1"> <GradientStop Color="Blue" Offset="0"></GradientStop> <GradientStop Color="White" Offset="0.5"></GradientStop> <GradientStop Color="Red" Offset="1"></GradientStop> </LinearGradientBrush> </Window.Resources> <Window.Background> <StaticResource ResourceKey="backgroundBrush" /> </Window.Background> <Grid> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <Button Background="{StaticResource buttonBackground}" BorderBrush="{StaticResource borderBrush}" Margin="5" Height="28"> <Image Height="21" Source="Images\zoom.gif"></Image> </Button> <Button Background="{StaticResource buttonBackground}" BorderBrush="{StaticResource borderBrush}" Margin="5" Height="28"> <Image Height="21" Source="Images\previous.gif"></Image> </Button> <Button Background="{StaticResource buttonBackground}" BorderBrush="{DynamicResource borderBrush}" Margin="5" Height="28"> <Image Height="21" Source="Images\next.gif"></Image> </Button> </StackPanel> </Grid> </Window> |
一个资源能够被以StaticResource或者DynamicResource的方式来引用,这是以标记扩展(StaticResource/DynamicResource Markup Extension)来标注的。去以StaticResource仍是以DynamicResource来引用一个资源,这取决于:
· 你是以什么样的方式来为你的应用建立资源的:每一个Page?一个Application?一个松散XAML文件?仍是一个只包含资源定义的程序集?
· 须要在运行时更新你的资源吗?
· 查找资源的行为—向前查询?
· 资源自己的行为和属性
3. Static Resource – 静态资源
StaticResource仅仅会被应用一次---在第一次须要资源时加载。并且这种引用方式不支持向前加载,全部的资源定义必须在引用以前定义。StaticResource一般用在:
· 设计的APP是将全部的资源放入Page或者App这个级别的Resource Dictionary中的,并且不须要在运行时从新计算—例如只保存一些松散文件,逻辑资源的声明等。
· 不须要给DependencyObject或者Freezable的对象设置属性。
· Resource Dictionary将被编译进DLL.
· 须要给不少的Dependency Property赋值。
将一个资源以Static Resource引用,须要用到Static Resource Markup Extension。它在已经定义的资源中查询特定key的value为XAML的某个属性赋值。这个查询的行为与load-time查找相似,在当前Page的XAML中或者全部Application的Resources中查找,并在运生成运行时对象。
XAML Attribute Usage
<object property=”StaticResource key}” … /> |
XAML Object Element Usage
<object> <object.Property> <StaticResource ResourceKey = “key” …/> </object.Property> </object> |
前者是咱们常常会用到的方式,好比给一个属性赋予一个早先定义的话刷格式:
<Button Background="{StaticResource buttonBackground}" BorderBrush="{StaticResource borderBrush}" Margin="5" Height="28"> |
然后者咱们在前面的例子中也看到了,用法稍微特殊,其实在这个状况下这个资源确定是一个逻辑资源,至关于一段声明对象的代码。好比给window设背景:
<Window.Background> <StaticResource ResourceKey="backgroundBrush" /> </Window.Background> |
Static Resource的查找行为
· 首先检查此对象自己的Resources集合内是否有匹配值(根据ResourceKey)
· 其次会在逻辑树中向上搜寻父元素的Resource Dictionary.
· 最后会检查Root级别的好比Page,Window,Application等。
4. Dynamic Resource – 动态资源
与Static Resource不一样的是,Dynamic Resource能够在程序运行时从新评估/计算资源来生成对应的对象/值,它支持向前引用,只要请求的key在整个应用程序内的任何Resources Dictionary定义过就能够被加载。若是有多个相同的key存在,则最后搜索到的资源为有效。
动态资源经常使用于如下状况:
· 资源直到运行时才能被取定其值的。这些包含想系统资源,或者经过用户交互/用户能够设定的值。例如你能够用Setter Property语法来引用一些系统资源像SystemColors, SystemFonts等,这些是真正的Dynamic Resource,由于他们是来自用户的运行环境。
· 在Custom control中有建立/引用主题风格的需求.
· 在运行过程当中调整(好比添加或者合并)ReourceDictionary.
· 须要向前引用的场景。
· 建立的Style的值与当前用户设定的主题或其余设定有关的。
· 运行过程当中可能更改逻辑树的次序的。
下面的代码片断演示了如何在XAML中引用SystemFonts,这须要用DynamicResource标记:
<Style x:Key="SimpleFont" TargetType="{x:Type Button}"> <Setter Property = "FontSize" Value= "{DynamicResource {x:Static SystemFonts.IconFontSizeKey}}"/> <Setter Property = "FontWeight" Value= "{DynamicResource {x:Static SystemFonts.MessageFontWeightKey}}"/> <Setter Property = "FontFamily" Value= "{DynamicResource {x:Static SystemFonts.CaptionFontFamilyKey}}"/> </Style> |
Dynamic Resource的查找行为
· 首先遍历请求对象自己定义的Resources集合。
· 而后遍历逻辑树上当前请求对象的父对象,直到遍历到Root(如Page.Reources, Window.Resources, UserControl.Resources等)
· 随后会遍历应用程序的Resources(即Application.Resources)
· 进而会Check当前激活的Theme的资源。
· 最后才会去遍历System Resources.
在程序中你能够经过myWindow.Resources[“key”]的方式来直接访问一个资源。另外,WPF还提供了TryFindResource(key)和FindResource(key)来支持资源搜索。FindResource方法在没找到资源的状况下会触发ResourceReferenceKeyNotFoundException异常。
其实经过上边的示例咱们能够很清楚的看到,在使用静态资源的地方咱们每每均可以使用动态资源,他们并无什么合适与否之说,而选择它们中的哪个,彻底取决于你是否须要资源的使用者发现更新。咱们能够再来比较一下两者的区别:
· 对于资源的更新会反映在那些使用了动态资源的元素上,这是他们最主要的区别。
· 性能上:由于动态资源要跟踪变化,因此须要占用更多的资源。而静态资源每每是在window或page加载以后来引用,动态资源会改善加载时间。但静态资源在使用时却会有些许性能的提高。
· 动态资源只能设置依赖属性只,而静态资源能够在任何地方使用。好比,咱们能够声明一个逻辑资源把它看成一个元素来用,而动态资源却没法作到:
<Window x:Class="WpfApplication1.Window3" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window3" Height="300" Width="300"> <Window.Resources> <Button Background="Blue" Margin="5" Height="28" x:Key="prev"> <Image Height="21" Source="Images\previous.gif"></Image> </Button> </Window.Resources> <Grid> <Button Height="20" Width="70" Content="Content" /> <StaticResource ResourceKey="prev" /> </Grid> </Window> |
· 当你在XAML中使用StaticResource时,是不支持Forward Reference的,也就是说任何资源必须在XAML文件中声明以后才可使用。若是是在同一个元素中定义,则只能使用Dynamic Resource。
<Window x:Class="WpfApplication1.Window3" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window3" Height="300" Width="300" Background="{DynamicResource winBackground}"> <Window.Resources> <Button Background="Blue" Margin="5" Height="28" x:Key="prev"> <Image Height="21" Source="Images\previous.gif"></Image> </Button> <LinearGradientBrush x:Key="winBackground" StartPoint="0,0" EndPoint="1,1"> <GradientStop Color="Blue" Offset="0"></GradientStop> <GradientStop Color="White" Offset="0.5"></GradientStop> <GradientStop Color="Red" Offset="1"></GradientStop> </LinearGradientBrush> </Window.Resources> <Grid> <Button Height="20" Width="70" Content="Content" /> <StaticResource ResourceKey="prev" /> </Grid> </Window> |
5. 资源的应用
前变也已经说过能够引用别的程序集的资源,那么到底如何引用呢?另外,咱们都是在说XAML中引用资源,那么代码中又该如何去作呢?咱们又一般会在什么地方定义资源呢?这里就来讲一下这几个问题,以及某些特殊状况下的定义。
· 共享资源
默认状况下,当有一个资源被引用到多个地方是,使用的都是同一个对象实例,这一般是理想的行为。但你一样也能够把x:Shared=”False”来让每一个引用资源的地方都生成一个不一样的对象实例,这样能够独立进行修改。这一般用于多逻辑资源的声明。
· 程序代码中定义和应用资源
在代码中定义一个新的Resource,你须要首先获得一个ResourceDictionary的实例,而后再建立一个新的资源并将这个资源加入到ResourceDictionary的实例中。而在访问资源时,你须要用到myWindow.Resources[“key”]或者object.FindResource(key)函数。注意myWindows是你当前window的实例,而在用FindResource时,前边的object表明的是这个资源所在的ResourceDictionary的父对象。
private void Window_Loaded(object sender, RoutedEventArgs e) { Window3 window = new Window3(); window.Resources.Add("buttonBackground", new SolidColorBrush(Color.FromRgb(0,255,0))); window.Resources.Add("borderBrush", new SolidColorBrush(Color.FromRgb(255, 0, 0)));
btnContent.Background = (Brush)window.FindResource("buttonBackground"); btnContent.BorderBrush = (Brush)window.FindResource("borderBrush"); } |
注意在找不到资源时会抛出一个ResourceReferenceKeyNotFoundException异常,因此尽可能调用TryFindResource方法更好些,若是失败将会返回null.
上边的例子是针对StaticResource来讲的,它就至关于这段代码:
<Button x:Name="btnContent" Canvas.Left="50" Background="{StaticResource buttonBackground}" BorderBrush="{StaticResource borderBrush}" Content="Content" /> |
|
但对于DynamicResource来讲,须要调用这个元素的SetResourceReference方法来更新依赖属性的绑定。下边的两端代码是相等的:
<Button x:Name="btnContent" Canvas.Left="50" Background="{DynamicResource buttonBackground}" BorderBrush="{ DynamicResource borderBrush}" Content="Content" /> |
btnContent.SetResourceReference(Button.BackgroundProperty, "buttonBackground"); btnContent.SetResourceReference(Button.BorderBrushProperty, "borderBrush"); |
SetResourceReference是能够在资源被加载到某个Resource Dictionary以前调用的,即使是FindResource会失败,但引用的创建仍然有效。
· 从另外一个程序集中访问嵌入式资源
除了能够用特定的URI来访问别的程序中的二进制资源外,WPF能够从另一个程序集中获取逻辑资源,这得用到ComponentResourceKey标记。要使用ComponentResourceKey,每一个资源都必须有一个键名。而后你能够经过这样的方式访问:
<Button Background=”{DynamicResource {x:Static otherAssembly: MyClass.MyClassBrushKey }}” /> |
· Styles 和Implicit Keys
样式是最多见的一种资源,并且它老是被定义在Resource Dictionary中,为了来重用。Style其实就是一系列分组的Setter的集合,用来设定逻辑资源的属性值,它有一种比较特殊的情形就是Implicit Keys,能够不声明一个x:Key的名字,而只设置x:TargetType的值,这样面对的就是对于全部这个类型的控件都使用这个样式。下边的示例中x:Key的值其实就是type-Button。
<Style TargetType="Button"> <Setter Property="Background"> <Setter.Value> <LinearGradientBrush> <GradientStop Offset="0.0" Color="AliceBlue"/> <GradientStop Offset="1.0" Color="Salmon"/> </LinearGradientBrush> </Setter.Value> </Setter> <Setter Property="FontSize" Value="18"/> </Style> |
Style也是资源的一种---从某种意义上来讲,它很相似于咱们给普通HTML中的元素创建CSS. 对于Designer来讲可能轻松一些哦。
6. Resource Dictionary –资源字典
全部的资源项在最终都会被整合到Resource Dictionary中的,也就是说不管是FrameworkElement的Resources,仍是Window的Resources,仍是Application的Resources,仍是特定的ResourceDictionary中定义的resources在整个应用编译执行的时候实际上他们都在一块儿的做为可遍历集合共同存在于一个相对会话空间内的。
咱们也提到过Resource的key是能够被容许有相同的,这样在遍历不一样相对地址的Resource Dictionary时会根据StaticResource或者DynamicResource的lookup behavior来肯定哪一个有效。一般为了维护和灵活性的考虑,咱们一般会将Resource Dictionary文件分红好几个,但在某些场合下咱们只须要用其中某些资源,那么我么能够将资源从几个独立的文件中提取并合并,那么能够这么作:
<Window.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Diction1.xaml"></ResourceDictionary> <ResourceDictionary Source="Diction2.xaml"></ResourceDictionary> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Window.Resources> |
注意,在资源合并后,仍然会出现重复值的状况,那么最后取出的资源获胜。
7. Localization – 本地化
本地化和换肤其实都是在用ResourceDictionary来作文章的。说白了,Localization就是用不一样语言下取不一样事先设定好的资源来显示而已。要作到这些很容易,4步就能够轻松实现:
· 定义Resource Dictionary来包含不一样语言下要显示的资源项。
建立单独的Resource Dictionary文件,并以语言自己名字来命名,并把en-US来做为默认语言环境(这里顺便就命名为default.xaml了)
Default.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=mscorlib">
<sys:String x:Key="Title_PM">Project Manager</sys:String> <sys:String x:Key="Title_PL">Project Lead</sys:String> <sys:String x:Key="Title_SD">Senior Developer</sys:String> <sys:String x:Key="Title_SA">System Architecture</sys:String>
</ResourceDictionary> |
zh-CN.xaml (注意对.NET命名空间的引用)
<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=mscorlib">
<sys:String x:Key="Title_PM">项目经理</sys:String> <sys:String x:Key="Title_PL">项目主管</sys:String> <sys:String x:Key="Title_SD">资深开发工程师</sys:String> <sys:String x:Key="Title_SA">系统架构师</sys:String>
</ResourceDictionary> |
· 给应用程序添加默认资源:其实就是将默认的Resource Dictionary加入到Application的全局Resource里边。
<Application x:Class="WpfApplication1.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="Window1.xaml"> <Application.Resources>
<ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Language\default.xaml"/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary>
</Application.Resources> </Application> |
· 在Application启动时根据不一样语言来加载以语言命名的XAML文件(Resource Dictionary)。由于对于重名的资源,后来加载的资源将会胜出,因此以当前语言名加载的XAML文件中的资源项将会被引用。这就是多语言的本质!
public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e);
LoadLanguage(); }
private void LoadLanguage() { CultureInfo currentCultureInfo = CultureInfo.CurrentCulture; ResourceDictionary langRd = null; try { langRd = Application.LoadComponent( new Uri(@"Language\" + currentCultureInfo.Name + ".xaml", UriKind.Relative)) as ResourceDictionary; } catch { }
if (langRd != null) { if (this.Resources.MergedDictionaries.Count > 0) { this.Resources.MergedDictionaries.Clear(); } this.Resources.MergedDictionaries.Add(langRd); } } } |
· 在XAML中引用资源。
<TextBlock Canvas.Top="50" Width="100" Height="24" Text="{StaticResource Title_PM}" /> |
· 大功告成,运行程序你会看到默认的语言的显示:Project Manager.固然若是你的默认文化是英语的话。用程序换成中文试试结果?没问题,在LoadLanguage()以前更改语言便可:
base.OnStartup(e); CultureInfo info = new CultureInfo("zh-CN"); Thread.CurrentThread.CurrentCulture = info; Thread.CurrentThread.CurrentUICulture = info; LoadLanguage(); |
简单吧?呵呵。有关Resource的东西基本上就这么多了,换肤咱们再开辟另外一个话题来谈吧。这但是WPF够炫的Feature之一哦。。。。
一些与本文有关的小代码片断及Localization的示例请点击这里下载。
WPF经过资源来保存一些能够被重复利用的样式,对象定义以及一些传统的资源如二进制数据,图片等等,而在其支持上也更能体现出这些资源定义的优越性。好比经过ResourceDictionary的支持就能够经过资源来实现换肤功能,在ExpressionBlend中设计的酷炫造型也能够经过导出成资源来很容易的被程序员所引用,本地化的实现,访问另外程序集的嵌入式资源等等。这些都给咱们提供了丰富的手段经过资源访问架构来构建丰富的富媒体应用程序。本文简单讲解了WPF Resources的分类及其常见用法,并简单触及用ResourceDictionary来管理多个Resources文件(这是换肤的基础)。
在WPF中的资源不只依赖于核心.NET的资源系统,在其基础上也添加了对两种不一样资源类型的支持:二进制资源和逻辑资源。而对于这些资源类型的构建动做也有了更多的支持选项。
1. 二进制资源
二进制资源实际上是一些传统的资源项,好比位图,音频文件,视频文件,松散文件(Loose file)等等。对于这些资源项咱们能够将其存储为松散文件,或者编译进程序集中。这与传统的.NET程序实际上是相通的,但在WPF中提供了两种对二进制资源的构建选项:
· Resource: 将资源放入程序集中(若是是有本地化支持的话会编译到对应语言集的子程序集中。
· Content:将这个资源做为一个松散文件加入到程序集中,程序集会记录对应的文件是否存在及其路径。这就至关于咱们web开发中经常使用的构建动做。
对于MSBuild来讲这也是默认的构建类型,例如,
<Content Include="Images\Go.ico" /> <Content Include="Images\Go.jpg" /> <Content Include="Images\Go2.gif" /> <Content Include="Images\Go2.jpg" /> <Content Include="Images\information16.png" /> <Content Include="Images\pass16.png" /> <Content Include="Images\pass32.png" /> <Content Include="Images\unknown16.png" /> <Content Include="Images\warning.gif" /> <Content Include="Images\warning16.png" /> <EmbeddedResource Include="LoginForm.resx"> <SubType>Designer</SubType> <DependentUpon>LoginForm.cs</DependentUpon> </EmbeddedResource> <EmbeddedResource Include="OptionsForm.resx"> <SubType>Designer</SubType> <DependentUpon>OptionsForm.cs</DependentUpon> </EmbeddedResource> <EmbeddedResource Include="PageHistory.resx"> <SubType>Designer</SubType> <DependentUpon>PageHistory.cs</DependentUpon> </EmbeddedResource> |
上图所示是一个普通的WinForm应用程序的项目文件,对于添加到其内部的二进制资源文件其默认的构建动做即是Content-代表其做为一个松散文件存储,只要保证其对应路径的文件存在则能够自动加载(而无需再你的打包文件中必须包含)。而你也会看到EmbeddedResource构建动做,这是WinForm的构建动做,它和Resource构建动做很类似,会在程序集中嵌入一个二进制资源,可是WPF中由于嵌入式资源比WPF还要优先,因此须要尽可能避免使用。
之因此推荐使用Resource和Content构建类型是由于这样嵌入的资源能够很容易的在XAML中被引用,并且对于WPF的统一资源识别符也是专门针对这两种构建动做而设计的。相反地,对于EmbeddedResource构建动做嵌入的资源是不能在XAML中被引用的,除非自定义代码。
· 访问二进制资源
访问二进制资源最普通的就是对松散文件的访问,这和普通的.NET应用程序没什么两样,直接看例子吧:
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <Button Background="Yellow" BorderBrush="Red" Margin="5"> <Image Height="21" Source="zoom.gif"/> </Button> <Button Background="Yellow" BorderBrush="Red" Margin="5"> <Image Height="21" Source="defaultThumbnailSize.gif"/> </Button> <Button Background="Yellow" BorderBrush="Red" Margin="5"> <Image Height="21" Source="previous.gif"/> </Button> </StackPanel> |
上述的的松散文件只须要以Content构建加入项目便可. 这只是普通的访问方式,固然也是咱们经常使用的,但也有不少你可能须要用到哦,不妨看看:
资源URI |
资源 |
zoom.gif |
存放于当前程序集;或是添加到项目的松散文件。 |
Folder/zoom.gif |
若是内部有目录结构时加入相对目录结构。 |
C:\Images\zoom.gif |
绝对路径的松散文件 |
\\FileServer\Images\zoom.gif |
共享目录的松散文件 |
http://my.net/zoom.gif |
位于某个站点上的松散文件 |
AssemblyReference;Component/ResourceName |
访问嵌入到另一个程序集或EXE文件内的资源。Component是关键字,必须写。例如: MyDll;Component/Images/zoom.gif; |
pack://siteOfOrigin:,,,/Images/zoom.gif |
访问位于部署位置的资源。 |
2. 逻辑资源
逻辑资源是WPF特有的资源类型,它是存储在元素的Resources属性中的.NET对象,一般须要共享给多个子元素。换句话说,你能够声明一个SolidColorBrush对象看成一个逻辑资源,你也能够声明一个Style,而后再后续的XAML中简单经过{StaticResource ResourceName}来使用。资源定义须要有一个在ResourceDictionary中惟一的关键字x:Key(单独的ResourceDictionary中的键名不能够重复,多个ResourceDictionary中键名能够重复,会根据在逻辑数上的lookup的顺序来就近生效)。例以下边的逻辑资源声明:
<Window x:Class="WpfApplication1.Window2" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window2" Height="300" Width="400"> <Window.Resources> <SolidColorBrush x:Key="buttonBackground">Yellow</SolidColorBrush> <SolidColorBrush x:Key="borderBrush">Red</SolidColorBrush> <LinearGradientBrush x:Key="backgroundBrush" StartPoint="0,0" EndPoint="1,1"> <GradientStop Color="Blue" Offset="0"></GradientStop> <GradientStop Color="White" Offset="0.5"></GradientStop> <GradientStop Color="Red" Offset="1"></GradientStop> </LinearGradientBrush> </Window.Resources> <Window.Background> <StaticResource ResourceKey="backgroundBrush" /> </Window.Background> <Grid> <StackPanel Orientation="Horizontal" HorizontalAlignment="Center"> <Button Background="{StaticResource buttonBackground}" BorderBrush="{StaticResource borderBrush}" Margin="5" Height="28"> <Image Height="21" Source="Images\zoom.gif"></Image> </Button> <Button Background="{StaticResource buttonBackground}" BorderBrush="{StaticResource borderBrush}" Margin="5" Height="28"> <Image Height="21" Source="Images\previous.gif"></Image> </Button> <Button Background="{StaticResource buttonBackground}" BorderBrush="{DynamicResource borderBrush}" Margin="5" Height="28"> <Image Height="21" Source="Images\next.gif"></Image> </Button> </StackPanel> </Grid> </Window> |
一个资源能够被以StaticResource或者DynamicResource的方式来引用,这是以标记扩展(StaticResource/DynamicResource Markup Extension)来标注的。去以StaticResource仍是以DynamicResource来引用一个资源,这取决于:
· 你是以什么样的方式来为你的应用建立资源的:每一个Page?一个Application?一个松散XAML文件?仍是一个只包含资源定义的程序集?
· 须要在运行时更新你的资源吗?
· 查找资源的行为—向前查询?
· 资源自己的行为和属性
3. Static Resource – 静态资源
StaticResource仅仅会被应用一次---在第一次须要资源时加载。并且这种引用方式不支持向前加载,全部的资源定义必须在引用以前定义。StaticResource一般用在:
· 设计的APP是将全部的资源放入Page或者App这个级别的Resource Dictionary中的,并且不须要在运行时从新计算—例如只保存一些松散文件,逻辑资源的声明等。
· 不须要给DependencyObject或者Freezable的对象设置属性。
· Resource Dictionary将被编译进DLL.
· 须要给不少的Dependency Property赋值。
将一个资源以Static Resource引用,须要用到Static Resource Markup Extension。它在已经定义的资源中查询特定key的value为XAML的某个属性赋值。这个查询的行为与load-time查找相似,在当前Page的XAML中或者全部Application的Resources中查找,并在运生成运行时对象。
XAML Attribute Usage
<object property=”StaticResource key}” … /> |
XAML Object Element Usage
<object> <object.Property> <StaticResource ResourceKey = “key” …/> </object.Property> </object> |
前者是咱们常常会用到的方式,好比给一个属性赋予一个早先定义的话刷格式:
<Button Background="{StaticResource buttonBackground}" BorderBrush="{StaticResource borderBrush}" Margin="5" Height="28"> |
然后者咱们在前面的例子中也看到了,用法稍微特殊,其实在这个状况下这个资源确定是一个逻辑资源,至关于一段声明对象的代码。好比给window设背景:
<Window.Background> <StaticResource ResourceKey="backgroundBrush" /> </Window.Background> |
Static Resource的查找行为
· 首先检查此对象自己的Resources集合内是否有匹配值(根据ResourceKey)
· 其次会在逻辑树中向上搜寻父元素的Resource Dictionary.
· 最后会检查Root级别的好比Page,Window,Application等。
4. Dynamic Resource – 动态资源
与Static Resource不一样的是,Dynamic Resource能够在程序运行时从新评估/计算资源来生成对应的对象/值,它支持向前引用,只要请求的key在整个应用程序内的任何Resources Dictionary定义过就能够被加载。若是有多个相同的key存在,则最后搜索到的资源为有效。
动态资源经常使用于如下状况:
· 资源直到运行时才能被取定其值的。这些包含想系统资源,或者经过用户交互/用户能够设定的值。例如你能够用Setter Property语法来引用一些系统资源像SystemColors, SystemFonts等,这些是真正的Dynamic Resource,由于他们是来自用户的运行环境。
· 在Custom control中有建立/引用主题风格的需求.
· 在运行过程当中调整(好比添加或者合并)ReourceDictionary.
· 须要向前引用的场景。
· 建立的Style的值与当前用户设定的主题或其余设定有关的。
· 运行过程当中可能更改逻辑树的次序的。
下面的代码片断演示了如何在XAML中引用SystemFonts,这须要用DynamicResource标记:
<Style x:Key="SimpleFont" TargetType="{x:Type Button}"> <Setter Property = "FontSize" Value= "{DynamicResource {x:Static SystemFonts.IconFontSizeKey}}"/> <Setter Property = "FontWeight" Value= "{DynamicResource {x:Static SystemFonts.MessageFontWeightKey}}"/> <Setter Property = "FontFamily" Value= "{DynamicResource {x:Static SystemFonts.CaptionFontFamilyKey}}"/> </Style> |
Dynamic Resource的查找行为
· 首先遍历请求对象自己定义的Resources集合。
· 而后遍历逻辑树上当前请求对象的父对象,直到遍历到Root(如Page.Reources, Window.Resources, UserControl.Resources等)
· 随后会遍历应用程序的Resources(即Application.Resources)
· 进而会Check当前激活的Theme的资源。
· 最后才会去遍历System Resources.
在程序中你能够经过myWindow.Resources[“key”]的方式来直接访问一个资源。另外,WPF还提供了TryFindResource(key)和FindResource(key)来支持资源搜索。FindResource方法在没找到资源的状况下会触发ResourceReferenceKeyNotFoundException异常。
其实经过上边的示例咱们能够很清楚的看到,在使用静态资源的地方咱们每每均可以使用动态资源,他们并无什么合适与否之说,而选择它们中的哪个,彻底取决于你是否须要资源的使用者发现更新。咱们能够再来比较一下两者的区别:
· 对于资源的更新会反映在那些使用了动态资源的元素上,这是他们最主要的区别。
· 性能上:由于动态资源要跟踪变化,因此须要占用更多的资源。而静态资源每每是在window或page加载以后来引用,动态资源会改善加载时间。但静态资源在使用时却会有些许性能的提高。
· 动态资源只能设置依赖属性只,而静态资源能够在任何地方使用。好比,咱们能够声明一个逻辑资源把它看成一个元素来用,而动态资源却没法作到:
<Window x:Class="WpfApplication1.Window3" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window3" Height="300" Width="300"> <Window.Resources> <Button Background="Blue" Margin="5" Height="28" x:Key="prev"> <Image Height="21" Source="Images\previous.gif"></Image> </Button> </Window.Resources> <Grid> <Button Height="20" Width="70" Content="Content" /> <StaticResource ResourceKey="prev" /> </Grid> </Window> |
· 当你在XAML中使用StaticResource时,是不支持Forward Reference的,也就是说任何资源必须在XAML文件中声明以后才可使用。若是是在同一个元素中定义,则只能使用Dynamic Resource。
<Window x:Class="WpfApplication1.Window3" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" Title="Window3" Height="300" Width="300" Background="{DynamicResource winBackground}"> <Window.Resources> <Button Background="Blue" Margin="5" Height="28" x:Key="prev"> <Image Height="21" Source="Images\previous.gif"></Image> </Button> <LinearGradientBrush x:Key="winBackground" StartPoint="0,0" EndPoint="1,1"> <GradientStop Color="Blue" Offset="0"></GradientStop> <GradientStop Color="White" Offset="0.5"></GradientStop> <GradientStop Color="Red" Offset="1"></GradientStop> </LinearGradientBrush> </Window.Resources> <Grid> <Button Height="20" Width="70" Content="Content" /> <StaticResource ResourceKey="prev" /> </Grid> </Window> |
5. 资源的应用
前变也已经说过能够引用别的程序集的资源,那么到底如何引用呢?另外,咱们都是在说XAML中引用资源,那么代码中又该如何去作呢?咱们又一般会在什么地方定义资源呢?这里就来讲一下这几个问题,以及某些特殊状况下的定义。
· 共享资源
默认状况下,当有一个资源被引用到多个地方是,使用的都是同一个对象实例,这一般是理想的行为。但你一样也能够把x:Shared=”False”来让每一个引用资源的地方都生成一个不一样的对象实例,这样能够独立进行修改。这一般用于多逻辑资源的声明。
· 程序代码中定义和应用资源
在代码中定义一个新的Resource,你须要首先获得一个ResourceDictionary的实例,而后再建立一个新的资源并将这个资源加入到ResourceDictionary的实例中。而在访问资源时,你须要用到myWindow.Resources[“key”]或者object.FindResource(key)函数。注意myWindows是你当前window的实例,而在用FindResource时,前边的object表明的是这个资源所在的ResourceDictionary的父对象。
private void Window_Loaded(object sender, RoutedEventArgs e) { Window3 window = new Window3(); window.Resources.Add("buttonBackground", new SolidColorBrush(Color.FromRgb(0,255,0))); window.Resources.Add("borderBrush", new SolidColorBrush(Color.FromRgb(255, 0, 0)));
btnContent.Background = (Brush)window.FindResource("buttonBackground"); btnContent.BorderBrush = (Brush)window.FindResource("borderBrush"); } |
注意在找不到资源时会抛出一个ResourceReferenceKeyNotFoundException异常,因此尽可能调用TryFindResource方法更好些,若是失败将会返回null.
上边的例子是针对StaticResource来讲的,它就至关于这段代码:
<Button x:Name="btnContent" Canvas.Left="50" Background="{StaticResource buttonBackground}" BorderBrush="{StaticResource borderBrush}" Content="Content" /> |
|
但对于DynamicResource来讲,须要调用这个元素的SetResourceReference方法来更新依赖属性的绑定。下边的两端代码是相等的:
<Button x:Name="btnContent" Canvas.Left="50" Background="{DynamicResource buttonBackground}" BorderBrush="{ DynamicResource borderBrush}" Content="Content" /> |
btnContent.SetResourceReference(Button.BackgroundProperty, "buttonBackground"); btnContent.SetResourceReference(Button.BorderBrushProperty, "borderBrush"); |
SetResourceReference是能够在资源被加载到某个Resource Dictionary以前调用的,即使是FindResource会失败,但引用的创建仍然有效。
· 从另外一个程序集中访问嵌入式资源
除了能够用特定的URI来访问别的程序中的二进制资源外,WPF能够从另一个程序集中获取逻辑资源,这得用到ComponentResourceKey标记。要使用ComponentResourceKey,每一个资源都必须有一个键名。而后你能够经过这样的方式访问:
<Button Background=”{DynamicResource {x:Static otherAssembly: MyClass.MyClassBrushKey }}” /> |
· Styles 和Implicit Keys
样式是最多见的一种资源,并且它老是被定义在Resource Dictionary中,为了来重用。Style其实就是一系列分组的Setter的集合,用来设定逻辑资源的属性值,它有一种比较特殊的情形就是Implicit Keys,能够不声明一个x:Key的名字,而只设置x:TargetType的值,这样面对的就是对于全部这个类型的控件都使用这个样式。下边的示例中x:Key的值其实就是type-Button。
<Style TargetType="Button"> <Setter Property="Background"> <Setter.Value> <LinearGradientBrush> <GradientStop Offset="0.0" Color="AliceBlue"/> <GradientStop Offset="1.0" Color="Salmon"/> </LinearGradientBrush> </Setter.Value> </Setter> <Setter Property="FontSize" Value="18"/> </Style> |
Style也是资源的一种---从某种意义上来讲,它很相似于咱们给普通HTML中的元素创建CSS. 对于Designer来讲可能轻松一些哦。
6. Resource Dictionary –资源字典
全部的资源项在最终都会被整合到Resource Dictionary中的,也就是说不管是FrameworkElement的Resources,仍是Window的Resources,仍是Application的Resources,仍是特定的ResourceDictionary中定义的resources在整个应用编译执行的时候实际上他们都在一块儿的做为可遍历集合共同存在于一个相对会话空间内的。
咱们也提到过Resource的key是能够被容许有相同的,这样在遍历不一样相对地址的Resource Dictionary时会根据StaticResource或者DynamicResource的lookup behavior来肯定哪一个有效。一般为了维护和灵活性的考虑,咱们一般会将Resource Dictionary文件分红好几个,但在某些场合下咱们只须要用其中某些资源,那么我么能够将资源从几个独立的文件中提取并合并,那么能够这么作:
<Window.Resources> <ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Diction1.xaml"></ResourceDictionary> <ResourceDictionary Source="Diction2.xaml"></ResourceDictionary> </ResourceDictionary.MergedDictionaries> </ResourceDictionary> </Window.Resources> |
注意,在资源合并后,仍然会出现重复值的状况,那么最后取出的资源获胜。
7. Localization – 本地化
本地化和换肤其实都是在用ResourceDictionary来作文章的。说白了,Localization就是用不一样语言下取不一样事先设定好的资源来显示而已。要作到这些很容易,4步就能够轻松实现:
· 定义Resource Dictionary来包含不一样语言下要显示的资源项。
建立单独的Resource Dictionary文件,并以语言自己名字来命名,并把en-US来做为默认语言环境(这里顺便就命名为default.xaml了)
Default.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=mscorlib">
<sys:String x:Key="Title_PM">Project Manager</sys:String> <sys:String x:Key="Title_PL">Project Lead</sys:String> <sys:String x:Key="Title_SD">Senior Developer</sys:String> <sys:String x:Key="Title_SA">System Architecture</sys:String>
</ResourceDictionary> |
zh-CN.xaml (注意对.NET命名空间的引用)
<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=mscorlib">
<sys:String x:Key="Title_PM">项目经理</sys:String> <sys:String x:Key="Title_PL">项目主管</sys:String> <sys:String x:Key="Title_SD">资深开发工程师</sys:String> <sys:String x:Key="Title_SA">系统架构师</sys:String>
</ResourceDictionary> |
· 给应用程序添加默认资源:其实就是将默认的Resource Dictionary加入到Application的全局Resource里边。
<Application x:Class="WpfApplication1.App" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" StartupUri="Window1.xaml"> <Application.Resources>
<ResourceDictionary> <ResourceDictionary.MergedDictionaries> <ResourceDictionary Source="Language\default.xaml"/> </ResourceDictionary.MergedDictionaries> </ResourceDictionary>
</Application.Resources> </Application> |
· 在Application启动时根据不一样语言来加载以语言命名的XAML文件(Resource Dictionary)。由于对于重名的资源,后来加载的资源将会胜出,因此以当前语言名加载的XAML文件中的资源项将会被引用。这就是多语言的本质!
public partial class App : Application { protected override void OnStartup(StartupEventArgs e) { base.OnStartup(e);
LoadLanguage(); }
private void LoadLanguage() { CultureInfo currentCultureInfo = CultureInfo.CurrentCulture; ResourceDictionary langRd = null; try { langRd = Application.LoadComponent( new Uri(@"Language\" + currentCultureInfo.Name + ".xaml", UriKind.Relative)) as ResourceDictionary; } catch { }
if (langRd != null) { if (this.Resources.MergedDictionaries.Count > 0) { this.Resources.MergedDictionaries.Clear(); } this.Resources.MergedDictionaries.Add(langRd); } } } |
· 在XAML中引用资源。
<TextBlock Canvas.Top="50" Width="100" Height="24" Text="{StaticResource Title_PM}" /> |
· 大功告成,运行程序你会看到默认的语言的显示:Project Manager.固然若是你的默认文化是英语的话。用程序换成中文试试结果?没问题,在LoadLanguage()以前更改语言便可:
base.OnStartup(e); CultureInfo info = new CultureInfo("zh-CN"); Thread.CurrentThread.CurrentCulture = info; Thread.CurrentThread.CurrentUICulture = info; LoadLanguage(); |
简单吧?呵呵。有关Resource的东西基本上就这么多了,换肤咱们再开辟另外一个话题来谈吧。这但是WPF够炫的Feature之一哦。。。。
一些与本文有关的小代码片断及Localization的示例请点击这里下载。