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. Swagger Editor 本地搭建

    看了很多文章,怎么本地安装都比较乱,一番折腾,最后终于成功本地搭建Swagger Editor,记录如下(Windows 7): 进入命令行: (1)cd E:\Learning\AWS (2)git ...

  2. 网络知识_01:ISO七层模型

    一 IOS七层模型 1.1OSI的概念 Open System Interconnect开放系统互连参考模型,是由ISO(国际标准化组织)定义的.它是个灵活的.稳健的和可互操作的模型. 1.2OSI模 ...

  3. 006 管理Ceph的RBD块设备

    一, Ceph RBD的特性 支持完整和增量的快照 自动精简配置 写时复制克隆 动态调整大小 二.RBD基本应用 2.1 创建RBD池 [root@ceph2 ceph]# ceph osd pool ...

  4. Netty堆外内存泄漏排查,这一篇全讲清楚了

    上篇文章介绍了Netty内存模型原理,由于Netty在使用不当会导致堆外内存泄漏,网上关于这方面的资料比较少,所以写下这篇文章,专门介绍排查Netty堆外内存相关的知识点,诊断工具,以及排查思路提供参 ...

  5. $[SHOI2007]$ 园丁的烦恼 二维数点/树状数组

    \(Sol\) 设一个矩阵的左上角为\((x_1,y_1)\),右下角为\((x_2,y_2)\),\(s_{x,y}\)是到\((1,1)\)二维前缀和,那么这个矩阵的答案显然是\(s_{x_2,y ...

  6. 【C++】递归 课后习题3-13、3-14

    // // main.cpp // 3-13递归Fibonacci级数 // // Created by T.P on 2018/3/21. // Copyright © 2018年 T.P. All ...

  7. JVM系列(三):java的垃圾回收机制

    java垃圾回收机制介绍    上一篇讲述了JVM的内存模型,了解了到了绝大部分的对象是分配在堆上面的,我们在编码的时候并没有显示的指明哪些对象需要回收,但是程序在运行的过程中是会一直创建对象的,之所 ...

  8. Linux Centos7 环境搭建Docker部署Zookeeper分布式集群服务实战

    Zookeeper完全分布式集群服务 准备好3台服务器: [x]A-> centos-helios:192.168.19.1 [x]B-> centos-hestia:192.168.19 ...

  9. 【5min+】 什么?原来C#还有这两个关键字

    系列介绍 简介 [五分钟的dotnet]是一个利用您的碎片化时间来学习和丰富.net知识的博文系列.它所包含了.net体系中可能会涉及到的方方面面,比如C#的小细节,AspnetCore,微服务中的. ...

  10. js滑动导航栏点击后居中效果

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...