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. IPv4数据报格式及其语义

    一.IP数据报的格式如下图所示 版本 首部长度 服务类型 数据报长度 16比特标识 标志 13比特片偏移 寿命 上层协议 首部检验和 32比特源IP地址 32比特目的IP地址 选项(如果有的话) 数据 ...

  2. K:缓存数据库双写数据一致性方案

    对于缓存和数据库双写,其存在着数据一致性的问题.对于数据一致性要求较高的业务场景,我们通常会选择使用分布式事务(2pc.paxos等)来保证缓存与数据库之间的数据强一致性,但分布式事务的复杂性与对资源 ...

  3. iOS-NSNotificationCenter通知原理解析

    一.基本概念 NSNotification和NSNotificationCenter是使用观察者模式来实现的用于跨层传递消息. NSNotificationCenter采用单例模式. 二.基本实现 通 ...

  4. 0014 标签显示模式:display(重点)

    目标: 理解 标签的三种显示模式 三种显示模式的特点以及区别 理解三种显示模式的相互转化 应用 实现三种显示模式的相互转化 2.1 什么是标签显示模式 什么是标签的显示模式? 标签以什么方式进行显示, ...

  5. Django简介、安装和入门

    python三大主流Web框架 Django 优点:大而全,自身携带的组件和功能特别特别多,类似于航空母舰 缺点:过于笨重,所需功能不多时,Django依然提供这些功能,占据内存 Flask 优点:小 ...

  6. HDU3652 B-number 题解 数位DP

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3652 题目大意: 求区间 \([1, n]\) 范围内包含连续的数位"13"并且能 ...

  7. 敏捷开发流程之Scrum:3个角色、5个会议、12原则

    本文主要从Scrum的定义和目的.敏捷宣言.Scrum中的人员角色.Scrum开发流程.敏捷的12原则等几方面帮助大家理解Scrum敏捷开发的全过程. 一.Scrum的定义和目的 Scrum是一个用于 ...

  8. 写 Java 这么久了,来编译个 JDK 玩玩儿吧

    你每天写的 Java 代码都需要 JDK 的支持,都要跑在 JVM 上,难道你就不好奇 JDK 长什么样子吗.好奇,就来编译并实现一个自己的 JDK 吧. 本次编译环境 macOS 10.12,编译的 ...

  9. Java框架之Spring01-IOC-bean配置-文件引入-注解装配

    Spring 框架,即framework.是对特定应用领域中的应用系统的部分设计和实现的整体结构.就相当于让别人帮你完成一些基础工作,它可以处理系统很多细节问题,而且框架一般是成熟,稳健的. Spri ...

  10. 固定表头的table

    在前端的开发过程中,表格时经常使用的一种展现形式.在我的开发过程中,当数据过多时,最常用的一种方式就是分页,但是有些地方还是需要滚动.通常的table 会随着滚动,导致表头看不见.一下是我找到的一种固 ...