Microsoft .NET 框架资源基础 ---摘自:msdn

Chris Sells
Sells Brothers Consulting

摘要:Chris Sells 讨论无类型清单资源和有类型资源,它们是受 Microsoft .NET 框架支持的两种资源。他定义了这两种资源,并介绍了如何在您自己的应用程序中使用它们。

下载 winforms02202003.exe 示例文件。

假设要在应用程序中通过从文件加载位图来设置窗体的背景图像:

public Form1() {...// Load a file from the file systemthis.BackgroundImage =new Bitmap(@"C:\WINDOWS\Web\Wallpaper\Azul.jpg");
}

该代码的问题是,并非所有 Microsoft Windows 的安装实例都有 Azul.jpg,即使是那些确实具有该文件的安装实例,该文件可能也不在安装实例的相同位置。即使您与应用程序一起交付该图片,节省空间的用户也可能决定删除它,这会导致您的应用程序出错。确保图片或任何文件与代码在一起的唯一安全方式是将它作为资源嵌入并加载。

清单资源

资源是在编译时添加到程序集中的。例如,如果您使用命令行编译器,则可以使用 /resource 开关嵌入资源:

C:\>csc.exe myApp.cs /resource:c:\windows\web\wallpaper\Azul.jpg

/resource 开关将文件作为资源嵌入,嵌入时使用文件名(没有路径)作为资源名称。文件嵌入到程序集的清单 资源集中。程序集的清单由一组作为程序集一部分的元数据组成。该元数据的一部分是与每个嵌入资源关联的名称和数据。执行 ildasm 时,可以在清单部分看见程序集清单资源的列表,如图 1 所示。

C:\>ildasm.exe myApp.exe

winforms02202003-fig01

图 1. ildasm 显示嵌入资源

可以像 ildasm 一样枚举清单资源的列表,这需要使用 System.Reflection.Assembly 类的 GetManifestResourceNames 方法:

using System.Reflection;
...
// Get this type's assembly
Assembly assem = this.GetType().Assembly;// Enumerate the assembly's manifest resources
foreach( string resourceName in assem.GetManifestResourceNames() ) {MessageBox.Show(resourceName);
}

一旦通过枚举清单资源或硬编码一个您想要的清单资源而知道了清单资源的名称,就可以通过 Assembly 类的 GetManifestResourceStream 方法将该清单资源作为原始字节流进行加载,如下所示:

using System.IO;public Form1() {...// Get this type's assemblyAssembly assem = this.GetType().Assembly;// Get the stream that holds the resource// NOTE1: Make sure not to close this stream!// NOTE2: Also be very careful to match the case//        on the resource name itselfStream stream =assem.GetManifestResourceStream("Azul.jpg");// Load the bitmap from the streamthis.BackgroundImage = new Bitmap(stream);
}

因为资源可以像类型名称一样有冲突,所以最好用资源自己的“命名空间”来嵌入资源,该操作可以使用 /resource 开关的扩展格式来完成:

C:\>csc myApp.cs /resource:c:\...\azul.jpg,ResourcesApp.Azul.jpg

注意在要嵌入的文件名的逗号后面使用的备用资源名称。备用资源名称允许您为资源任意地提供时间嵌套名称,不管文件名是什么。它是设置在程序集中的备用名称,如图 2 所示。

winforms02202003-fig02

图 2. 使用备用名称的嵌入资源

下面是使用备用名称的更新后的资源加载代码:

public Form1() {...// Get this type's assemblyAssembly assem = this.GetType().Assembly;// Load a resource with an alternate nameStream stream =assem.GetManifestResourceStream("ResourcesApp.Azul.jpg");// Load the bitmap from the streamthis.BackgroundImage = new Bitmap(stream);
}

为了更方便,如果您的资源和加载资源的类碰巧使用了相同的命名空间,则可以将类的类型作为可选的第一参数传递给 GetManifestResourceStream

namespace ResourcesApp {public class Form1 : Form {public Form1() {...// Get this type's assemblyAssembly assem = this.GetType().Assembly;// Load the resource using a namespace// Will load resource named "ResourcesApp.Azul.jpg"Stream stream =assem.GetManifestResourceStream(this.GetType(), "Azul.jpg");// Load the bitmap from the streamthis.BackgroundImage = new Bitmap(stream);}...}
}

GetManifestResourceStream 将使用如下格式编写资源名称:

<namespace>.<fileName>

在加载某些类型(比如 Bitmap 类)时,使用类型和文件名也是有用的,这样可以通过提供构造函数避免由您自己打开流:

namespace ResourcesApp { public class Form1 : Form { public Form1() { ... // Get this type's assembly Assembly assem = this.GetType().Assembly; // Load the bitmap directly from the manifest resources this.BackgroundImage = new Bitmap(this.GetType(), "Azul.jpg"); } ... }}

Visual Studio .NET 中的清单资源

如果(大多数情况下)您使用 Visual Studio?NET 来开发和构建程序集,则用命令行嵌入清单资源的方法不可能非常吸引您。这种情况下,您可以将资源添加到 Windows 窗体项目中,该方法将把合适的命令行参数传递给编译器。

要将资源添加到项目中,请在 Solution Explorer 中右键单击项目,然后选择 Add New Item,并选择您想作为资源嵌入的文件。文件将复制到项目的目录中,但仍然不会被嵌入。要使文件作为资源嵌入,请右键单击文件,并选择 Properties,然后将 Build Action 从 Content(默认)更改为 Embedded Resource,如图 3 所示。

winforms02202003-fig03

图 3. 将文件的 Build Action 设置为 Embedded Resource

这种嵌入资源的方法会使 Visual Studio .NET 为您创建一个备用资源名,其组成类似这样:

<defaultNamespace>.<folderName>.<fileName>

资源名称的默认命名空间部分是项目本身的默认命名空间,它是通过 Solution Explorer->(右键单击)->Properties->Common Properties->General->Default Namespace 来设置的。由于这是在生成新类时,新类得到的相同命名空间,所以这就使通过使用类型和部分资源名称来加载资源变得很方便。如果文件碰巧位于项目的子文件夹中,就需要在文件夹名称中包括子文件夹,并用点替换反斜杠。例如,一个名为 Azul.jpg 的位图位于项目根下面的 foo\bar 文件夹中,要加载它就需要这样做:

// If this code called within the ResourcesApp.Form1 class,
// and the file is \foo\bar\Azul.jpg,
// will load ResourcesApp.foo.bar.Azul.jpg
this.BackgroundImage =new Bitmap(this.GetType(), "foo.bar.Azul.jpg");

有类型资源

尽管文件有扩展名,但清单资源是在没有类型信息的情况下嵌入的。例如,如果 Azul.jpg 文件的名称实际上是 Azul.quux,这对于 Bitmap 类来说是没有差别的,因为这个类将通过查看数据本身来确定其类型(JPEG、PNG、GIF 等)。这就需要由您来将每个资源的类型正确映射为加载该资源所需的对象的类型。

但如果您愿意多走一步,则可以用一个类型来标记资源。.NET 框架支持用于资源的一组扩展元数据,其中包括两种格式的 MIME 类型信息,一个是文本格式,另一个是二进制格式。这两种格式都有内置的读取器,以便在运行时取得类型正确的资源。

基于文本的格式是特定于.NET 框架的 XML 格式,称为 ResX(.resx 文件)。不考虑其 XML 基础,该格式不是专门为人工阅读而设计的(XML 格式很少是这样的)。但是,Visual Studio .NET 仍然为 .resx 文件提供了一个基本编辑器。要将新的 .resx 文件添加到 Visual Studio .NET 项目中,请从 Project 菜单中选择 Add New Item,然后选择 Assembly Resource File 模板,如图 4 所示。

winforms02202003-fig04

图 4. 将 .resx 文件添加到项目中

到写本文时为止,即使空的 .resx 文件也是 42 行 XML,而其中大多数是架构信息。架构允许 .resx 文件中有任意数目的项,每项都有名称、值、注释、类型和 MIME 类型。图 5 显示了有两个项的 .resx 文件,即名为 MyString 的字符串和名为 MyImage 的图像。

winforms02202003-fig05

图 5. 设计器的数据视图中简单的 .resx 文件

遗憾的是,只有字符串项能够在 .resx 编辑器的数据视图中实际进行编辑。任何二进制数据都需要手动直接输入到 XML 中(而且只能是 base64 编码)。因此,直接使用 .resx 文件只对字符串资源有用(尽管间接使用会使 .resx 文件对任何种类的数据都非常有用,我们随后将讨论这一点)。

来自 System.Resources 命名空间的 ResXResourceReader 类将分析 XML 文件,并公开一组命名的、有类型的值。要取得具体的项需要查找它:

using System.Collections;
using System.Resources;
...
public Form1() {...using( ResXResourceReader reader =new ResXResourceReader(@"Resource1.resx") ) {foreach( DictionaryEntry entry in reader ) {if( entry.Key.ToString() == "MyString" ) {// Set form caption from string resourcethis.Text = entry.Value.ToString();}}}
}

使用 Add New Item 对话框将 .resx 文件添加到项目中会使该文件作为 Embedded Resource 添加进项目,而编译项目时则会导致 .resx 数据作为嵌套资源 嵌入(“嵌套资源”是分组到命名容器中的资源)。容器的名称与作为资源添加的任何文件相同,只是不使用 .resx 扩展名,使用 .resource 扩展名。假定一个项目的默认命名空间是 ResourcesApp 而 .resx 文件名为 Resources1.resx,则嵌套资源的容器名为 ResourcesApp.Resources1.resx,如图 6 中的 ildasm 所示。

winforms02202003-fig06

图 6. 嵌入的 .resources 文件

.resources 扩展名来自于在将 .resx 文件作为资源嵌入之前 Visual Studio .NET 处理该文件时所使用的工具。工具名称是 resgen.exe,它用来将 .resx XML 格式“编译”为二进制格式。可以手动将 .resx 文件编译成 .resources 文件,如下所示:

C:\> resgen.exe Resource1.resx

在将 .resx 文件编译成 .resources 文件以后,就可以使用 System.Resources 命名空间中的 ResourceReader 来枚举它:

using( ResourceReader reader =new ResourceReader(@"Resource1.resources") ) {foreach( DictionaryEntry entry in reader ) {string s = string.Format("{0} ({1})= '{2}'",entry.Key, entry.Value.GetType(), entry.Value);MessageBox.Show(s);}
}

除了类的名称和输入格式,ResourceReader 类的使用方法与 ResXResourceReader 相同,包括都不能随机访问命名项。

所以,虽然您可以将 .resx 文件转换成 .resources 文件,并使用 /resource 编译器命令行开关嵌入它,但容易得多的方法是直接在项目中让 Visual Studio .NET 接受被标记为 Embedded Resources 的 .resx 文件,然后将它编译进 .resources 文件并嵌入它,如图 4、图 5 和图 6 所示。一旦将 .resources 文件捆绑为资源,访问 .resources 文件中的资源就只需执行两个步骤的过程:

// 1. Load embedded .resources file
using( Stream stream =assem.GetManifestResourceStream(this.GetType(), "Resource1.resources") ) {// 2. Find resource in .resources fileusing( ResourceReader reader = new ResourceReader(stream) ) {foreach( DictionaryEntry entry in reader ) {if( entry.Key.ToString() == "MyString" ) {// Set form caption from string resourcethis.Text = entry.Value.ToString();}}}
}

因为 ResourceReaderResXResourceReader 都需要该两步过程才能找到具体的资源,因此 .NET 框架提供了 ResourceManager 类,该类公开了一个更简单的使用模型。

资源管理器

ResourceManager 类也来自 System.Resources 命名空间,该类包装了 ResourceReader,用于在构造时枚举资源,并使用其名称公开它们:
public Form1() {...// Get this type's assemblyAssembly assem = this.GetType().Assembly;// Load the .resources file into the ResourceManager// Assumes a file named "Resource1.resx" as part of the projectResourceManager resman =new ResourceManager("ResourcesApp.Resource1", assem);// Set form caption from string resourcethis.Text = (string)resman.GetObject("MyString"); // The hard waythis.Text = resman.GetString("MyString"); // The easy way
}

用来查找 .resources 文件的命名方式与命名任何其他种类的资源相同(注意追加到 Resource1.resources 文件中的项目默认命名空间的使用方法),只是 .resources 扩展名是假定的,并且不能包括在名称中。为了更方便,如果您碰巧将一个 .resx 文件命名为类型名称,则 .resources 文件和程序集的名称将从类型确定:

// Use the type to determine resource name and assembly
ResourceManager resman = new ResourceManager(this.GetType());

一旦已经创建了资源管理器的实例,就可以通过使用 GetObject 方法并强制转换为合适的类型,从而按名称找到嵌套资源。如果使用 .resx 文件来处理字符串资源,则可以使用 GetString 方法,该方法将执行到 System.String 类型的强制转换。

设计器资源

缺少用于 .resx 文件的合适的编辑器使它们在使用除字符串资源以外的任何其他资源时非常困难。您不仅必须通过手动编写代码才能在运行时输入数据,而且无法在设计时看见资源的使用情况;例如,窗体的背景图像。

幸运的是,设计器再次在这里帮助了我们。如果打开 Visual Studio .NET Solution Explorer,并选择 Show All Files 按钮,您将看见每个组件(无论它是窗体、控件还是简单的组件)都有相应的 .resx 文件。这是为了让资源与组件的属性保持关联,这种关联是在 Property Browser 中设置的。例如,如果设置窗体的 BackgroundImage 属性,那么不仅在设计器中窗体将显示背景图像,而且窗体的 .resx 文件将包含该图像的对应项。同样,如果在相同窗体上设置 PictureBox 控件的 Image 属性,则 .resx 文件同样会增大以便包括该资源。这两项都可以在图 7 中看到。

winforms02202003-fig07

图 7. 组件的 .resx 文件

每个组件的 .resx 文件将作为 .resources 文件进行编译和嵌入,就像已经将您自己的 .resx 文件添加到项目中一样,这将使资源能够在运行时被组件使用。除了组件的 .resx 文件中的项之外,设计器还会将代码添加到 InitializeComponent 中,以便加载组件的资源管理器,并使用从资源获得的对象来填充组件的属性:

namespace ResourcesApp {public class Form1 : Form {...private void InitializeComponent() {ResourceManager resources = new ResourceManager(typeof(Form1));...this.pictureBox1.Image =(System.Drawing.Bitmap)resources.GetObject("pictureBox1.Image");...this.BackgroundImage =(System.Drawing.Bitmap)resources.GetObject("$this.BackgroundImage");...}}
}

注意 ResourceManager 对象是使用组件的类型来构造的,该类型用来构造组件的 .resources 资源名称。还要注意设计器在命名资源时所使用的命名约定。对于组件字段上的属性,名称的格式是:

<fieldName>.<propertyName>

对于组件本身的属性,名称格式是:

$this.<propertyName>

如果您想添加供组件本身使用的自定义字符串属性,您可以这样做,但要确保与设计器生成的名称格式不同。

我们所处的位置

Microsoft .NET 框架支持两种资源 — 无类型清单资源和有类型资源。通过将文件的 Build Action 设置为 Embedded Resource,可以让 Visual Studio .NET 支持无类型清单资源,并通过 .resx 文件(可以是自定义文件或作为组件资源的备份存储)支持有类型资源。清单资源的好处是,它们可在 IDE 中直接编辑,而有类型资源需要做特别的工作才能编辑,但可提供有类型访问。两种资源类型都有某些严格的命名要求,所以在编写方法调用来加载它们时要格外小心。

转载于:https://www.cnblogs.com/newsun/archive/2007/12/28/1018539.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/363838.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HDU 5025Saving Tang Monk BFS + 二进制枚举状态

3A的题目&#xff0c;第一次TLE&#xff0c;是因为一次BFS起点到终点状态太多爆掉了时间。 第二次WA&#xff0c;是因为没有枚举蛇的状态。 解体思路&#xff1a; 因为蛇的数目是小于5只的&#xff0c;那就首先枚举是否杀死每只蛇即可。 然后多次BFS&#xff0c;先从起点到第一…

ansible模块之yum、pip、service、corn、user、group

ansible相关模块 yum rpm 和yum 的区别 rpm&#xff1a;全称redhat package manager &#xff08;红帽包管理器&#xff09; 不能解决包之间的依赖关系 yum&#xff1a;可以解决依赖关系yum 源配置[rootlocalhost ~]# cat /etc/yum.repos.d/epel.repo [epel] nameExtra Package…

linux nuttx 环境搭建,ubuntu14.04 nuttx开发环境的搭建

origin: http://blog.csdn.net/hunter168_wang/article/details/529145451. NUTTX编译用 toolchain的下载与配置开发环境&#xff1a;64位Ubuntu 14.04 系统编译NuttX用的toolchain下载地址&#xff1a;https://launchpadlibrarian.net/268330503/gcc-arm-none-eabi-5_4-2016q2…

Elasticsearch-kopf导览

当我需要一个插件来显示Elasticsearch的集群状态时&#xff0c;或者需要深入了解通常为经典插件elasticsearch-head所达到的索引时。 由于有很多建议&#xff0c;而且似乎是非官方的继任者&#xff0c;所以我最近对elasticsearch-kopf进行了更详细的介绍 。 我喜欢它。 我不确…

文字阴影-CSS Text-Shadow

1<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">2<html xmlns"http://www.w3.org/1999/xhtml">3<head>4<title>文字阴影-CSS Text-Shad…

javascript动态创建radio button元素支持IE/Firefox

我们都知道在IE中创建表单元素可以有三种方式varoInput document.createElement("input");varoInput document.createElement("<input />");varoInput document.createElement("<input name />");在Firefox里面仅支持varoInput docu…

C# 只允许运行一个程序实例

using System; using System.Windows.Forms; using System.Runtime.InteropServices;//使用DllImport的必须。 using System.Diagnostics;//引入Process 类namespace 命名空间 {static class Program{private const int WS_SHOWNORMAL 1;[DllImport("User32.dll")]p…

如何在Android Studio中添加RecyclerView-v7支持包

首先右键你的项目然后选择 open module Settings 之后会出现这个界面&#xff0c;选择 Modules 下的app ,>>> Dependencies >>>点击右边的“” 选择第一项Library dependency 出现此界面&#xff0c;然后选择你所需要的 转载于:https://www.cnblogs.com/lcx9…

linux 时间会跳吗,linux系统时间暂时跳跃

此脚本将告诉您何时发生时间漂移以及进程树中的差异,如果这是由更改系统时间的进程引起的,则应该有助于识别此情况.它将打印到终端并登录到当前工作目录中的timedrift.log.#!/bin/basholdTime"$(date %s)"oldPsOutput"$(ps faux)"while true; dosleep 1;cu…

您从未听说过的Java 8的10个功能

Lambdas lambdas lambdas。 这是您在谈论Java 8时所听到的所有信息。但这只是一部分。 Java 8具有许多新功能-有些是功能强大的新类和习惯用语&#xff0c;而另一些则是从一开始就应该存在的功能。 我想介绍十个新功能&#xff0c;我认为这些功能绝对是值得了解的小宝石。 您肯…

浮动层图片鼠标指针移到自动放大

html code:1<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">2<html xmlns"http://www.w3.org/1999/xhtml">3<head>4<title>缔友计算机信…

透明的iframe

<iframe allowtransparency"true" src"about: " οnlοad"style.display block;"> 只要是两个属性&#xff1a; 1&#xff09; allowtransparency"true" 2&#xff09; src"about: " 或iframe 的风页设置成 在IE下…

koa2 mongdb 做后端接口的小demo

现在前端全栈里面有一种技术栈比较火 前端使用 vue 或者react 后端使用 koa2 mysql数据库 或者mongdb做数据储存 但是基本这样的全栈教程 都要收费 收费就收费吧 但是 有没有遇到非常好的教程 于是 准备硬着头皮看别人项目的源码 自己摸索 一步一步完成 koa mongdb的后端学习…

字符大小端aix linux,long, unsigned long不是跨平台的(慎用)

项目中用到long、long long等字段&#xff0c;遇到一些问题。先说得到的一些结论&#xff1a;大小端&#xff1a;Windows、Linux是小端&#xff0c;AIX是大端。sizeof(指针类型)程序位数/8。long、unsigned long不是跨平台的&#xff0c;一定要慎用。自己写了程序测试各平台下(…

Java构建工具:Ant vs. Maven vs Gradle

最初&#xff0c;Make是唯一可用的构建工具。 后来通过GNU Make进行了改进。 但是&#xff0c;从那时起&#xff0c;我们的需求增加了&#xff0c;结果&#xff0c;构建工具也不断发展。 JVM生态系统主要由三个构建工具组成&#xff1a; 常春藤的 Apache Ant 马文 摇篮 An…

redis问题与解决思路

问题现象&#xff1a; 集群状态 1主 2从&#xff0c;主没有开启bgsave&#xff0c;从开启bgsave。现象所有redis可以访问&#xff0c;进行操作。主不断开始bgsave 1从停止bgsave。 主日志报错# Connection with slave XXXX lost. 从日志报错# Timeout receiving bulk data from…

Asp.Net中用javascript实现弹出窗口永远居中

//Asp.Net中用javascript实现弹出窗口永远居中functionShowDialog(url) { var iWidth600; //模态窗口宽度 var iHeight500;//模态窗口高度 var iTop(window.screen.height-iHeight)/2; var iLeft(window.screen.width-iWidth)/2; window.open(url,"Detail"…

Linux Vim 光标错位,技术|Vim 复制粘帖格式错乱问题的解决办法

有时候&#xff0c;复制文本(尤其是代码)到 Vim&#xff0c;会出现格式错乱的问题。看样子&#xff0c;应该是自动缩进惹得祸。本文不去深究原因&#xff0c;直接给出解决方法。1. paste 模式运行如下命令&#xff0c;进入 paste 模式&#xff1a;:set paste进入 paste 模式后&…

jQuery事件机制

1 事件操作 1.1 页面载入事件 $(document).ready(function(){// 在这里写你的代码... }); 或者 $(function($) { // 你可以在这里继续使用$作为别名... }); 1.2 事件绑定 on(eve,[sel],[data],fn) 1.7 在选择元素上绑定一个或多个事件的事件处理函数 bind(type,[dat…

struts中多个模块时,使用多个struts-config.xml文件之间时如何切换的!

1.配置web.xml文件&#xff0c;通知控制器&#xff1a; <!-----------------这个是默认的--------------------------> <init-param> <param-name>config</param-name> <param-value>/WEB-INF/struts-config.xml</param-valu…