WPF应用程序中的程序集资源与其他.NET应用程序中的程序集资源在本质上是相同的。基本概念是为项目添加文件,从而Visual studio可将其嵌入到编译过的应用程序的EXE或DLL文件中。WPF程序集资源与其他应用程序中的程序集资源之间的重要区别是引用他们的寻址系统不同。

  在前面章节已讨论过程序集资源的工作原理。因为每次编译应用程序时,项目中的每个XAML文件都转换为解析效率更高的BAML文件。这些BAML文件作为独立资源嵌入到程序集中。添加自己的资源同样很容易。

一、添加资源

  可通过向项目添加文件,并在Properties窗口中将其Build Action属性设置为Resource来添加自己的资源。这是需要完成的全部工作——这确实是好消息。

  为更加合理地组织资源,可在项目中创建子文件夹(在Solution Explorer中右击项目名称,然后选择Add|New Folder菜单项),然后使用这些子文件夹组织不同类型的资源。

  以这种方式添加的资源易于更新。只需要替换文件并重新编译应用程序即可。例如,可在Windows浏览器中将所有新文件复制到指定文件夹中。只要替换在项目中包含的文件的内容,就不必在Visual Studio中再采取任何其他特殊步骤(除了实际编译应用程序外)。

  为成功地使用程序集资源,务必注意以下两点:

  不能将Build Action属性错误地设置为Embedded Resource。尽管所有程序集资源都被定义为嵌入的资源,但Embedded Resource生成操作会在另一个更难访问的位置放置二进制数据。在WPF应用程序中,假定总是使用Resource生成类型。

  不要将Project Properties窗口中使用Resource选项卡。WPF不支持这种类型的资源URI。

  好奇的编程人员自然希望了解嵌入到程序集中的资源到底发生了什么变化。WPF将他们和其他BAML资源合并到单独的流中。单独的资源流使用以下格式命名AssemblyName.g.resources。

  如果想要实际查看在编译过的程序集中嵌入的资源,可使用反编译工具。例如,使用Reflector(http://reflector.net)的更出色工具来深入挖掘资源。

  除所有图像和音频文件外,还可看到用于应用程序中窗口的BAML资源。在WPF中,文件中的空格不会引起问题,因为Visual Studio足够智能,它能够正确地略过他们。当应用程序被编译过之后,你可能还会注意到文件名变成了小写形式。

二、检索资源

  显然,添加资源非常容易,但到底如何使用他们呢?可以采用多种方法来使用资源。

  低级方法是检索封装数据的StreamResourceInfo对象,然后决定如何使用该对象。可通过代码,使用静态方法Application.GetResourceStream()完成该工作。例如,下面的代码为winter.jpg图像获取StreamResourceInfo对象:

StreamResourceInfo sri=Application.GetResourceStream(new Uri("image/winter.jpg",UriKind.Relative));

  一旦得到StreamResourceInfo对象,就可以得到两部分信息。ContentType属性返回一个描述数据类型的字符串——在该例中是image/jpg。Stream属性返回一个UnmanagedMemoryStream对象,可使用该对象读取数据,一次读取一个字节。

  GetResourceStream()的确是一个很有用的辅助方法,它封装了ResourceManager类和ResourceSet类。这些类是.NET Framework资源体系的核心,自从.NET 1.0开始就提供了这些类。如果不使用GetResourceStream()方法,就需要具体访问AssemblyName.g.resources资源流(这是存储所有WPF资源的地方),并查找所需的对象。下面是完成这一操作的非常简单的代码:

Assembly assembly=Assembly.GetAssembly(this.GetType());
string resourceName=assembly.GetName().Name+".g";
ResourceManager rm=new ResourceManager(resourceName,assembly); using(ResourceSet set=rm.GetResourceSet(CultureInfo.CurrentCulture,tur,true))
{
UnmanagedMemoryStream s;
s=(UnmanagedMemoryStream)set.GetOjbect("images/winter.jpg",true);
}

  通过ResourceManager类和ResourceSet类还可完成其他一些Application类自身不能完成的工作。例如,下面的代码片段会向你现实在AssemblyName.g.resources资源流中所有嵌入资源的名称:

Assembly assembly=Assembly.GetAssembly(this.GetType());
string resourceName=assembly.GetName().Name+".g";
ResourceManager rm=new ResourceManager(resourceName,assembly);
using(ResourceSet set=rm.GetResourceSet(CultureInfo.CurrentCulture,true,ture))
{
foreach(DictionaryEntry res in set)
{
MessageBox.Show(res.Key.ToSting());
}
}

 虽然GetResourceStream()方法可提供帮助,但直接检索资源还可能会遇到麻烦,问题是使用该方法得到的相对低级的UnmanagedMemoryStream对象,该对象本身没有什么用处,需要将它转换成一些更有意义的数据,例如具有属性和方法的高级对象。

  WPF提供了几个专门使用资源的类。这些类不要求提取资源(这非常混乱且不是类型安全的),他们使用资源的名称访问资源。例如,如果希望在WPF的Image元素中显示Blue.jpg图像,可使用下面的标记:

<Image Source="Images/Blue.jpg"></Image>

  注意反斜杠变成了正斜杠,因为这是WPF作用URI的约定(实际上这两种方式都可行,但为了连贯起见,建议使用正斜杠)。

  可使用代码完成相同的工作。对于Image元素,只需要将Source属性设置为BitmapImage对象,该对象使用URI确定希望显示的图像的位置,可以像下面这样指定完全限定的文件路径:

img.Source = new BitmapImage(new Uri("d:\images\winter.jpg",));

  但如果使用相对URI,就可从程序集中提取不同资源,并将他们传递给图像,而且不需要使用UnmanagedMemoryStream对象:

img.Source = new BitmapImage(new Uri("images/winter.jpg", UriKind.Relative));

  该技术通过在基本应用程序URI的末尾处加上images/winter.jpg构造了URI。大多数情况下不需要考虑URI语法——只要遵循相对URI,剩下的工作就由程序集负责了。然而有些情况下,更详细理解URI系统的非常重要的,当希望访问嵌入到另一个程序集中额资源时更是如此。

三、pack URI

  WPF使用pack URI语法寻址编译过的资源(比如用于页面的BAML)。上一节的Image对象和标签使用相对URI来引用资源,如下所示:

  images/winter.jpg

  这与下面更繁琐的绝对URI是等效的:

  pack://application:,,,/images/winter.jpg

  当为一幅图像设置源时可使用这种绝对URI,尽管这种方法没有任何优点:

img.Source = new BitmapImage(new Uri("pack://application:,,,/images/winter.jpg"));

  pack URI语法来自XPS(XML Paper Specification,XML页面规范)标准。它看起来非常奇怪,因为它在一个URI中嵌入了另一个URI。三个逗号实际上时三个转义的斜杠。换句话说,上面显示的包含应用成功需URI的pack URI是以application:///开头的。

  位于其他程序集中的资源

  使用pack URI还可检索嵌入到另一个库中的资源(换句话说,在应用程序中使用的DLL程序集中的资源)。这种情况下需要使用如下语言:

pack://application:,,,/AssemblyName;component/ResourceName

  例如,如果图像呗嵌入到引用的名为ImageLibrary的程序集中,将需要使用如下URI:

img.Source=new BitmapImage(new Uri("pack://application:,,,/ImageLibrary;component/images/winter.jpg"));

  或从更实用的角度看,可使用等价的相对URI:

img.Source=new BitmapImage(new Uri("ImageLibrary;component/images/winter.jpg",UriKind.Relative));

  如果使用强命名的程序集,可使用包含版本和/或公钥标记的限定程序集引用代替程序集的名称。使用分号隔离每段信息,并在版本号数字之前添加字符v.下面是一个使用版本号的示例:

image.Source=new BitmapImage(new Uri("ImageLibrary;v1.25;component/images/winter.jpg",UriKind.Relative));

  下面的示例同时使用了版本号和公钥标记:

image.Source=new BitmapImage(new Uri("ImageLibrary;v1.25;dc642a7f5bd64912;component/images/winter.jpg",UriKind.Relative));

四、内容文件

  当嵌入文件作为资源时,会将文件放到编译过的程序集中,并且可以确保文件总是可用的。对于部署而言这是理想选择,并且可避免可能存在的问题。然而在有些情况下,使用这种方法并不方便:

  •   希望改变资源文件,又不想重新编译应用程序。
  •   资源文件非常大。
  •   资源文件是可选的,并且可以不随程序集一起部署。
  •   资源是声音文件。

  显然,可事业能够应用程序部署文件,并为应用程序添加代码,进而从硬盘驱动器中读取这些文件来解决该问题。然而,WPF还有更方便的选择,使这一过程更加容易管理。可将这些未编译的文件专门标记为内容文件。

  不能将内容文件嵌入到程序集中。然而,WPF为程序集添加了AssemblyAssociatedContentFile特性,公告每个内容文件的存在。该特性还记录了每个内容文件相对可执行文件的位置(指示内容文件是否和可执行文件位于同一文件夹中,或者位于某个子文件夹中)。最方便的是,当为能够理解资源的元素(如Image类)使用内容文件时,可使用相同的URI系统。

  为测试该技术,为项目添加声音文件,在Solution Exporer中选择该文件,并在Properties窗口中将Build Action属性改为Content,确保将Copy to Output Directory属性设置为Copy Always,以确保当生产项目时将声音文件复制到输出目录中。

  现在可使用相对URI,将MediaElement元素指向内容文件:

<MediaElement Name="Sound" Source="Sounds/start.wav" LoadedBehavior="Manual"></MediaElement>

  

【WPF学习】第二十八章 程序集资源的更多相关文章

  1. “全栈2019”Java多线程第二十八章:公平锁与非公平锁详解

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java多 ...

  2. “全栈2019”Java第二十八章:数组详解(上篇)

    难度 初级 学习时间 10分钟 适合人群 零基础 开发语言 Java 开发环境 JDK v11 IntelliJ IDEA v2018.3 文章原文链接 "全栈2019"Java第 ...

  3. 风炫安全web安全学习第二十八节课 CSRF攻击原理

    风炫安全web安全学习第二十八节课 CSRF攻击原理 CSRF 简介 跨站请求伪造 (Cross-Site Request Forgery, CSRF),也被称为 One Click Attack 或 ...

  4. 风炫安全WEB安全学习第二十五节课 利用XSS键盘记录

    风炫安全WEB安全学习第二十五节课 利用XSS键盘记录 XSS键盘记录 同源策略是浏览器的一个安全功能,不同源的客户端脚本在没有明确授权的情况下,不能读写对方资源.所以xyz.com下的js脚本采用a ...

  5. Gradle 1.12用户指南翻译——第二十八章. Jetty 插件

    其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Github上的地址: https://g ...

  6. 风炫安全web安全学习第二十九节课 CSRF防御措施

    风炫安全web安全学习第二十九节课 CSRF防御措施 CSRF防御措施 增加token验证 对关键操作增加token验证,token值必须随机,每次都不一样 关于安全的会话管理(SESSION) 不要 ...

  7. 风炫安全WEB安全学习第二十六节课 XSS常见绕过防御技巧

    风炫安全WEB安全学习第二十六节课 XSS常见绕过防御技巧 XSS绕过-过滤-编码 核心思想 后台过滤了特殊字符,比如说

  8. 风炫安全WEB安全学习第二十四节课 利用XSS钓鱼攻击

    风炫安全WEB安全学习第二十四节课 利用XSS钓鱼攻击 XSS钓鱼攻击 HTTP Basic Authentication认证 大家在登录网站的时候,大部分时候是通过一个表单提交登录信息. 但是有时候 ...

  9. 风炫安全WEB安全学习第二十三节课 利用XSS获取COOKIE

    风炫安全WEB安全学习第二十三节课 利用XSS获取COOKIE XSS如何利用 获取COOKIE 我们使用pikachu写的pkxss后台 使用方法: <img src="http:/ ...

随机推荐

  1. 大数据hadoop集群部署(一)

     环境系统配置  JAVA虚拟机的安装

  2. DOCKER学习_002:Docker的容器管理

    一 Docker的基本信息 前面已经安装了Docker,现在看一下已安装Docker的安装环境以及其他信息 1.1 系统环境 [root@docker-server3 ~]# uname -r -.e ...

  3. 对sql server查询速度的优化

    处理百万级以上的数据提高查询速度的方法: 1.应尽量避免在 where 子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描. 2.对查询进行优化,应尽量避免全表扫描,首先应考 ...

  4. 十三、springboot 优雅集成spring-boot-admin 实现程序监控

    前言 我们知道项目的监控是尤为重要的,但是我们如果用jdk 自带的jconsole 和jvisualvm 的话会非常繁琐,且界面不是很友好.之前我们使用了spring boot 项目,但是都没有对项目 ...

  5. 如何在Linux上创建,列出和删除Docker容器

    本篇文章介绍的内容是关于在Linux机器上创建,列出和删除docker容器,下面我们来看具体的内容. 1.启动Docker容器 使用下面的命令启动新的Docker容器.这将启动一个新的容器,并为你提供 ...

  6. $Loj10157$ 皇宫看守 树形$DP$

    loj Description 有一些宫殿,它们呈树形结构,相邻的宫殿之间可以互相望见.在一些宫殿设立士兵,使得所有的宫殿都有士兵或是被士兵望见.求最小士兵数. Sol 状态: f[x][0] 表示结 ...

  7. iOS颜色转换成图片的方法

    // 颜色转换为背景图片 - (UIImage *)imageWithColor:(UIColor *)color { CGRect rect = CGRectMake(0.0f, 0.0f, 1.0 ...

  8. 敏捷开发:代码Review

    热情高涨 代码走查作为一种流程形式,起初大家的参与热情非常高涨. 因为,自己可以学习到别人一些巧妙的思想,自己的代码和习惯都暴漏出来. 这个过程中不断地吸收和改正. 但是...... 我们一开始组织的 ...

  9. 1029 旧键盘 (20 分)C、Java、python

    题目描述 旧键盘上坏了几个键,于是在敲一段文字的时候,对应的字符就不会出现.现在给出应该输入的一段文字.以及实际被输入的文字,请你列出 肯定坏掉的那些键. 输入描述: 输入在2行中分别给出应该输入的文 ...

  10. static和final关键字

    static关键字 静态变量 静态变量:又称做类变量,也就是这个变量属于整个类,而不属于单个实例.类所有的实例共享静态变量,可以直接通过类名来访问它.静态变量在内存中只存在一份,当系统第一次加载类时, ...