深入浅出话资源

简介: 转自:http://blog.csdn.net/lisenyang/article/details/18312503我们把有用的东西称为资源。“兵马未动,粮草先行”-----程序中的各种数据就是算法的原料和粮草。

转自:http://blog.csdn.net/lisenyang/article/details/18312503

我们把有用的东西称为资源。“兵马未动,粮草先行”-----程序中的各种数据就是算法的原料和粮草。程序中可以存放数据的地方有很多,可以放在数据库里、可以存储在变量里。介于数据库存储和变量存储之间,我们还可以把数据存储在程序主体之外的文件里。外部文件与程序主体分离,这就有可能丢失或者损坏,编译器允许我们把外部文件编译进程序主体、称为程序主体不可分割的一部分。这就是传统意义上的程序资源(也称为二进制资源)。

WPF不但支持程序级的传统资源,同时还推出了独具特色的对象级资源,每个界面元素都可以携带自己的资源并可被自己的子级元素共享。比如后面的章节我们会讲到模板、程序样式和主题就经常放在对象资源里面。这样一来,在WPF程序中数据就分为4个等级存储了:数据库里的数据相当于存放在仓库里面,资源文件里的数据就相当于放进了旅行箱里,WPF对象资源里面的数据相当于存放在携带的背包里,变量里面的数据相当于拿在手里。

1.1       WPF对象资源的定义和查找

每个WPF界面元素都有一个名为Resource的属性,这个属性继承至FrameworkElement类,其类型为ResourceDictionary。ResourceDictionary能够以键值对的形式存储资源,当要使用到某个资源的时候,使用键值对的形式获取资源对象。在保存资源时,ResourceDictionary视资源对象为Object类型,所以再使用资源时先要对资源对象进行类型转换,XAML编译器能够根据Attribute自动识别资源类型,如果类型不对就会抛出异常,但在C#中检索到资源对象之后,类型转换的事情就只能由我们自己来做了。

ResourceDictionary可以存储任意类型的对象。在XAML代码中向Resource添加资源时需要把正确的命名空间引入到XAML代码中,让我们来看一个例子:

 

[html]  view plain copy print ?
 
 
  1. <Window x:Class="WpfApplication1.Window31"  
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
  4.         xmlns:sys="clr-namespace:System;assembly=mscorlib"  
  5.         Title="Window31" FontSize="16" SizeToContent="Height">  
  6.     <Window.Resources>  
  7.         <ResourceDictionary>  
  8.             <sys:String x:Key="str">沉舟侧畔千帆过,病树前头万木春。</sys:String>  
  9.             <sys:Double x:Key="db">3.1415926</sys:Double>  
  10.         </ResourceDictionary>  
  11.     </Window.Resources>  
  12.     <StackPanel>  
  13.         <TextBlock Text="{ StaticResource ResourceKey=str}"></TextBlock>  
  14.         <!--<TextBlock Text="{StaticResource ResourceKey=db}"></TextBlock>-->  
  15.     </StackPanel>  
  16. </Window>  


首先我们将System命名空间引入XAML代码中并映射为sys名称空间,然后在Windows.Resource里面添加了两个资源条目,一个是string类型,一个是double类型。最后我们用两个textBlock来消费这两个资源(被注释掉的代码因为数据类型不匹配而抛出异常)。程序运行效果如下图:

 

因为在XAML代码里面可以对集合类容及标签扩展进行简写,所以上面代码更常见的书写格式是这样:

 

[html]  view plain copy print ?
 
 
  1. <Window x:Class="WpfApplication1.Window31"  
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
  4.         xmlns:sys="clr-namespace:System;assembly=mscorlib"  
  5.         Title="Window31" FontSize="16" SizeToContent="Height">  
  6.     <Window.Resources>  
  7.             <sys:String x:Key="str">沉舟侧畔千帆过,病树前头万木春。</sys:String>  
  8.             <sys:Double x:Key="db">3.1415926</sys:Double>  
  9.     </Window.Resources>  
  10.     <StackPanel>  
  11.         <TextBlock Text="{ StaticResource str}"></TextBlock>  
  12.         <!--<TextBlock Text="{StaticResource ResourceKey=db}"></TextBlock>-->  
  13.     </StackPanel>  
  14. </Window>  

再查找资源时,先查找控件自己的Resource属性,如果没有这个资源程序会沿着逻辑树向上一级进行查找,如果连最顶端容器都没有这个资源,程序就会查找Application.Resource(也就是程序的顶级资源)。如果还没有找到,那么就只能抛出异常了。

 

这就好比每个界面元素都有自己的一个背包,里面可能装有各种各样的资源,使用的时候打开找一找,如果没有找到还可以去翻看上一层控件的背包,直至找到这个资源或报告没有这个资源为止。

如果想在C#代码里面使用XAML代码里面定义的资源,大概格式是这样:

 

[csharp]  view plain copy print ?
 
 
  1. private void Window_Loaded(object sender, RoutedEventArgs e)  
  2. {  
  3.     string text = (string)this.FindResource("str");  
  4.     txt0.Text = text;  
  5. }  


或者你明确知道资源放在那个资源字典里,就可以这样检索资源:

 

 

[csharp]  view plain copy print ?
 
 
  1. private void Window_Loaded(object sender, RoutedEventArgs e)  
  2. {  
  3.     string text = (string)this.Resources["str"];  
  4.     txt0.Text = text;  
  5. }  


你可能会想,如果把资源想CSS或者JS一样放在独立的文件夹里,使用时成套引用、重用时便于分发岂不更好?WPF的资源当然可以做到这一点;ResourceDictionary具有一个名为Source的属性,只要把包含资源定义的文件路径赋值给这个属性就一切搞定了!举个例子,http://wpf.codeplex.com中包含了很多官方/半官方的WPF资源,其中包括WPF工具包和一组非常漂亮的程序皮肤,这些皮肤以资源的形式放在XAML文件中,使用时仅需要将相应的XAML文件添加进项目并使用Source属性进行引用,你的程序就立刻变的光鲜照人。

 

 

[html]  view plain copy print ?
 
 
  1. <ResourceDictionary Source="ShinyRed.xaml">  
  2.               
  3. </ResourceDictionary>  

运行效果如下图:

 

1.2         且“动”且“静”用资源

当资源被存储进资源词典之后,我们可以使用两种方式来使用这些资源-----静态方式和动态方式。Static和Dynamic两个词都是我们的老朋友了,当这对词同时出现的时候Static指的是程序的非执行状态而Dynamic指的是程序的运行状态。对于资源的使用,Static和Dynamic也是这个意思。静态资源使用StackResource指的是程序载入内存时对资源的一次性使用,之后就不在去访问这个资源了;动态资源(DynamicResource)使用指的是在程序运行过程中仍然回去访问资源。显然如果你确定某些资源在程序初始化的时候只使用一次、之后不会再改变,就应该使用StaticResource,而程序运行过程中还有可能改变资源应该以DynamicResource形式使用。拿程序的主题来举例,如果程序的皮肤在运行过程中始终不变,以Static形式来使用资源就可以了。如果在程序运行过程中允许用户更改皮肤或者配色方案则必须使用DynamicResource来使用资源。

请看下面这个例子,我在Windows资源字典里放置了两个TextBlock类型资源,并分别以StaticResource和DynamicResource方式使用之:

 

[html]  view plain copy print ?
 
 
  1. <Window x:Class="WpfApplication1.Window32"  
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
  4.         Title="Window32" FontSize="16" WindowStyle="ToolWindow">  
  5.     <Window.Resources>  
  6.         <ResourceDictionary>  
  7.             <TextBlock x:Key="res1">海上生明月</TextBlock>  
  8.             <TextBlock x:Key="res2">海上生明月</TextBlock>  
  9.         </ResourceDictionary>  
  10.     </Window.Resources>  
  11.     <StackPanel>  
  12.         <Button Content="{StaticResource res1}" Margin="5"></Button>  
  13.         <Button Content="{DynamicResource res2}" Margin="5"></Button>  
  14.         <Button Content="Update" Margin="5" Click="Button_Click"></Button>  
  15.     </StackPanel>  
  16. </Window>  


界面上的第三个按钮负责在程序运行过程中对资源词典里面的两个资源进行改变:

 

 

[csharp]  view plain copy print ?
 
 
  1. private void Button_Click(object sender, RoutedEventArgs e)  
  2. {  
  3.     this.Resources["res1"] = new TextBlock() { Text="天涯共此时"};  
  4.     this.Resources["res2"] = new TextBlock() { Text = "天涯共此时" };  
  5. }  

实际上,因为第一个按钮是以静态方式使用资源,尽管资源已经更新它也不知道。运行程序,单击第三个按钮,效果如下图:

 

   

1.3       向程序集中添加二进制资源

对于资源这个概念,对于WPF初学者会感到迷惑,因为早在WPF出现之前Window应用程序就已经能够携带资源了。Windows应用程序资源的道理和WinZip或WinRAR压缩包的原理差不多,实际上是吧一些应用程序必须使用的资源和应用程序自身打包在一起,这样资源就不会意外丢死了(副作用就是应用程序体积会变大)。常见的应用程序资源有图标、图片、文本、音频、视频等,各种编程语言的编译器或者资源编译器都有能力把这些文件编译进目标文件(最终的.exe文件或者.dll文件)。资源文件在目标文件里以二进制数据形式存在、形成目标文件的资源段(Resource Section),使用时数据会被提取出来。

为了不把资源词典里的资源和应用程序里面内嵌的资源搞混,我们明确称呼资源词典里面的资源为“WPF资源”或“对象资源”,称呼应用程序内嵌资源为“程序集资源”或者“二进制资源”。特别提醒一点,WPF中写在<Application.Resource>...</Application.Resource>标签内的资源仍然是WPF资源而非二进制资源。

下面让我们看看如何向WPF程序中添加二进制资源并使用它们。

如果要添加的资源是字符串而非文件,我们可以使用应用程序名称空间下的Resources.resx资源文件。打开资源文件的方法是项目管理器中展开Properties文件夹,并双击下面的Resources.resx资源文件。如下图所示:

 

Resources.resx文件内容的组织形式也是“键-值”对,编译后,Resources.resx会形成Properties名称空间中的Resource类,使用这个类的方法或属性就能获取资源。为了让XAML编译器能够访问这个类,一定要把Resources.resx的访问级别由Internal改为public。利用资源文件编辑器,可以资源文件的字符串里添加两个条目,然后分别在XAML代码和C#代码中访问他们。

在XAML代码中使用Resources.resx中的资源,需要把程序的Properties名称映射为XAML名称空间,然后使用x:Static标签扩展来访问资源。

 

[html]  view plain copy print ?
 
 
  1. <Window x:Class="WpfApplication1.Window33"  
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
  4.         xmlns:prop="clr-namespace:WpfApplication1.Properties"  
  5.         Title="Window33" Height="300" Width="300">  
  6.     <Grid>  
  7.         <Grid.RowDefinitions>  
  8.             <RowDefinition Height="23" />  
  9.             <RowDefinition Height="4" />  
  10.             <RowDefinition Height="23" />  
  11.         </Grid.RowDefinitions>  
  12.         <Grid.ColumnDefinitions>  
  13.             <ColumnDefinition Width="Auto" />  
  14.             <ColumnDefinition Width="4" />  
  15.             <ColumnDefinition Width="*" />  
  16.         </Grid.ColumnDefinitions>  
  17.         <TextBlock x:Name="txtName" Text="{x:Static prop:Resources.userName}"></TextBlock>  
  18.         <TextBlock x:Name="txtPass" Grid.Row="2"></TextBlock>  
  19.         <TextBox BorderBrush="Black" Grid.Column="2"></TextBox>  
  20.         <TextBox BorderBrush="Black" Grid.Row="2" Grid.Column="2"></TextBox>  
  21.     </Grid>  
  22.      
  23. </Window>  

C#中访问Resources.resx的资源与使用一般的别无二致:

 

 

[csharp]  view plain copy print ?
 
 
  1. public Window33()  
  2. {  
  3.     InitializeComponent();  
  4.     this.txtPass.Text = Properties.Resources.pass;  
  5. }  

运行效果如下图:

 

使用Resources.resx最大的好处就是便于程序国际化,本地化。如果你想把界面改为英文版,只需要把资源里的值改为英文就可以了,如下图所示,因为在程序中访问资源使用的是资源的名,所以代码无需改动:

如果要添加的资源不是字符串,而是图标、图片、音频或者视屏。方法就不是使用Resources.resx了,WPF不支持这么做。在WPF使用外部文件作为资源,仅需要将其简单的放入项目即可。方法是在项目管理器上右击项目名称,在弹出的菜单里选择New-->NewFolder,按需要新建几层文件夹来存放资源,然后在恰当的文件夹上右击,在弹出的菜单里选择Add--->Existing Item...,在文件对话框里选择文件后单击Add按钮,文件就以资源的形式加入项目中了。

如果在程序里面添加一个MP3文件和一个图片文件,结果文件的体积会膨胀好几兆。如下图:

有一点特别提醒大家,如果想让外部文件编译进二进制资源,必须在属性窗口把文件的Build Action属性值设为Resource。并不是每种文件都会自动设置为Resource,比如图片文件会,MP3文件就不会,一般情况下,如果Build Action的值设为Resource,则Copy to Output Directory属性设置为Do Not Copy;如果不希望以资源的形式使用外部文件,可以把Build Action属性设置为None,而把Copy to Output Directory设置为Copy Always。另外,Build Action属性的下拉列表里面有一个颇具迷惑性的值Embeded Resource,不要选择这个值。

1.4                使用PACK URI路径访问二进制资源

WPF对二进制资源的访问有自己的一套方法,称为PACK URI路径。有时候死记硬背能够让读者快速学习又能帮助作者偷点懒。比如,WPF的PACK URI路径,你只需要记住这个格式就可以了:

 

[html]  view plain copy print ?
 
 
  1. pack://application,,,[/程序集名称;][可选版本号;][文件夹名称/][文件名称]  


而实际上pack://applicationi,,,可以省略、程序集名称和版本号常使用省略值,所以剩下的就只有这个了:

 

 

[html]  view plain copy print ?
 
 
  1. [文件夹名称/][文件名称]  

前面的例子中,我们向资源中添加了一张名为20090102191236877.gif的图片,它在项目中的路径是Resource/Image/20090102191236877.gif,原封不动,使用这个路径就可以访问到图片了。我们用这个图片填充一个<Image/>元素并把<image/>元素作为窗体的背景。

 

 

[html]  view plain copy print ?
 
 
  1. <Window x:Class="WpfApplication1.Window34"  
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
  4.         Title="Window34">  
  5.     <Grid>  
  6.         <Image Source="Resource/Image/20090102191236877.gif" x:Name="img0" Stretch="Fill"></Image>  
  7.     </Grid>  
  8. </Window>  

 

 

[html]  view plain copy print ?
 
 
  1. <Window x:Class="WpfApplication1.Window34"  
  2.         xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"  
  3.         xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"  
  4.         Title="Window34">  
  5.     <Grid>  
  6.         <Image Source="pack://application:,,,/Resource/Image/20090102191236877.gif" x:Name="img0" Stretch="Fill"></Image>  
  7.     </Grid>  
  8. </Window>  

与之等价的C#代码如下:

 

 

[csharp]  view plain copy print ?
 
 
  1. public Window34()  
  2. {  
  3.     InitializeComponent();  
  4.     Uri imageURI = new Uri(@"Resource/Image/20090102191236877.gif",UriKind.Relative);  
  5.     this.img0.Source = new BitmapImage(imageURI);  
  6. }  

 

 

[csharp]  view plain copy print ?
 
 
  1. public Window34()  
  2. {  
  3.     InitializeComponent();  
  4.     Uri imageURI = new Uri(@"pack://application:,,,/Resource/Image/20090102191236877.gif",UriKind.Absolute);  
  5.     this.img0.Source = new BitmapImage(imageURI);  
  6. }  


运行效果如下图所示:

 

在使用pack uri路径时有以下几点需要注意:

 

    • Pack URI使用的是从右向左的正斜线(/)表示路径。
    • 使用所略写意味着相对路径,C#代码中的UriKind必须为Relative而且代表根目录的/可以省略。
    • 使用完整写法时是绝对路径,C#代码中的UriKind必须为Absolute并且代表根目录的/不能省略。
    • 使用相对路径可以借助类似DOS的语法进行导航,比如./代表同级目录,../代表父级目录。
相关文章
|
4月前
|
Java
深入浅出Java多线程之创建与管理
【8月更文挑战第31天】Java世界中,多线程是提升程序性能的利器。本文将带你从零开始,一步步深入到Java的多线程世界,探索如何创建和管理这些小而强大的执行单元。我们将一起学习如何使用Thread类和Runnable接口来创建线程,以及如何通过Executor框架高效地管理它们。准备好让你的Java程序跑得更快了吗?让我们开始吧!
|
4月前
|
设计模式 数据采集 安全
掌握Java并发编程:从基础到高级
Java语言以其强大的并发处理能力而著称,在多核处理器日益普及的今天,有效利用并发编程技术可以显著提高应用程序的性能。本文将深入浅出地介绍Java并发编程的核心概念、实用工具类和设计模式,并结合实例展示如何在Java中实现高效的并发处理。无论你是初学者还是有经验的开发者,这篇文章都将为你开启Java并发编程的大门,带你领略它的奥妙与魅力。
49 3
|
4月前
|
Java API UED
深入Java并发编程:从基础到高级
【8月更文挑战第1天】在Java的世界中,理解并掌握并发编程是提升应用性能和响应能力的关键。本文将通过实际代码示例,从基础的线程创建到复杂的并发工具类的应用,逐步引导读者构建起对Java并发编程的全面认识。我们将一起探索如何有效管理线程、使用锁机制以及利用现代并发API来处理复杂的同步问题。
31 3
|
6月前
|
存储 安全 Java
全面详解Java并发编程:从基础到高级应用
全面详解Java并发编程:从基础到高级应用
55 1
|
6月前
|
存储 自然语言处理 编译器
技术好文共享:程序的基本概念
技术好文共享:程序的基本概念
|
7月前
|
Rust 安全 测试技术
Rust并发编程实践:快速入门系统级编程
Rust是一门现代的系统编程语言,它的设计目标是提供安全性、并发性和高性能。Rust的出现是为了解决其他编程语言在这些方面存在的一些问题和挑战。
|
C# 开发者 C++
【学习资源】C#初学者学习资源推荐
初学者学习C#的学习资源推荐, 包括网站,书籍与社区。
440 0
【学习资源】C#初学者学习资源推荐
|
自然语言处理 数据挖掘 Unix
R 语言是什么、R 的优势、资源|学习笔记
快速学习 R 语言是什么、R 的优势、资源。
199 1
R 语言是什么、R 的优势、资源|学习笔记
|
弹性计算 JSON 运维
资源编排基础知识|学习笔记
快速学习资源编排基础知识
资源编排基础知识|学习笔记
|
关系型数据库 网络安全 定位技术
常见概念和使用场景|学习笔记
快速学习常见概念和使用场景
常见概念和使用场景|学习笔记