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,一经查实,立即删除!

相关文章

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…

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

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

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

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>缔友计算机信…

字符大小端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…

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 模式后&…

kali vmtools 不能复制粘贴解决方法(绝对实用)

朋友问起怎么vm kali 2019怎么不能复制了&#xff0c;而且网上的方法大多不适合。我就在这儿记录一笔吧&#xff0c;方便大家。 之前发现最新kali复制粘贴不能用&#xff0c;后来发现一个奇妙的套路&#xff0c;不是共享文件夹。只需要把文件复制到命令行中&#xff0c;会出现t…

MineCraft和堆外内存

总览 MineCraft是一个很好的例子&#xff0c;说明何时堆外内存确实可以提供帮助。 关键要求是&#xff1a; 保留的数据大部分是一个简单的数据结构&#xff08;在我的世界的情况下&#xff0c;它的很多字节[]&#xff09; 堆外内存的使用可以隐藏在抽象中。 考试 我使用以下测…

kubernetes进阶之七:Service

1.概述 Service也是Kubernetes里的最核心的资源对象之一&#xff0c;Kubernetes里的每个Service其实就是我们经常提起的微服务架构中的一个“微服务”&#xff0c;之前我们所说的Pod、RC等资源对象其实都是为这节所说的“服务”------Kubernetes Service作“嫁衣”的。图1.12显…

Json Schema的使用

直接上案例&#xff1a; 在Web Api通讯中&#xff0c;客户端发送json数据&#xff0c;服务端反序列化json&#xff08;json与某个类形成对应关系&#xff09;&#xff0c;在某些情况下&#xff0c;需要校验其上传的json是否合法。 服务端是使用Json.net(newtonsoft.json)进行…

红帽企业版linux 7.4更新启动,红帽Linux企业版7.4 淘汰Btrfs文件系统

我们不得不承认Btrfs是一种古老的文件系统&#xff0c;当初(2007年)是由甲骨文宣布并进行中的COW(copy-on-write式)文件系统&#xff0c;意图取代Linux的ext。但是天不遂人愿&#xff0c;2011年8月9日&#xff0c;Fedora就决定Btrfs不再作为Fedora 16默认文件系统&#xff0c;走…

关于控件postback 后viewstate加载失败的问题

我写了一个控件Inherits TextBox&#xff0c;里面有一个复杂属性Tip&#xff0c;但每次postback的时候都说加载viewstate失败&#xff0c;除非我在!postback的情况下给Tip.xxx赋值. 下面我贴出代码&#xff0c;我已经搞了一天了&#xff0c;搞不出什么原因。 JTextBox控件 usin…

天猫浏览型应用的CDN静态化架构演变(转)

在天猫双11活动中&#xff0c;商品详情、店铺等浏览型系统&#xff0c;通常会承受超出日常数倍甚至数十倍的流量冲击。随着历年来双11流量的大幅增加&#xff0c;每年这些浏览型 系统都要面临容量评估、硬件扩容、性能优化等各类技术挑战。因此&#xff0c;架构方面的重点在于&…

查看您的Solr缓存大小:Eclipse Memory Analyzer

Solr使用不同的缓存来防止请求期间过多的IO访问和计算。 当索引不是很频繁发生时&#xff0c;您可以通过使用这些缓存来获得巨大的性能提升。 根据索引数据的结构和缓存的大小&#xff0c;它们可能会变得很大&#xff0c;并占用堆内存的很大一部分。 在本文中&#xff0c;我想展…

会话跟踪之Session

Session是服务端使用记录客户端状态的一种机制&#xff0c;Session使用简单&#xff0c;但是和Cookie相比&#xff0c;增加了服务器的存储压力【因为为了追求速度&#xff0c;服务器将Session放置在了内存中】。Cookie是保存在客户端的&#xff0c;然而Session是保存在服务器上…

在NIO.2中创建文件和目录

如今&#xff0c;大量的应用程序创建文件或目录的目的非常广泛。 无论是生成报告&#xff0c;导出配置文件还是仅存储一些数据&#xff0c;能够处理这些任务都非常重要。 创建文件和目录是使用文件系统时最常用的功能之一。 图书馆的这一部分进行了相当现代化。 这方面的更新包…

实现flash的图片切换效果【可以切换多个网页或者图片】

这个是得到改进后的代码&#xff0c;可以切换多个页面 需要完整代码的朋友可以留下email如需再添加切换页面&#xff0c;只要按照下边代码部分的样式添加内容即可切换导航td的id要顺序排那个div的TOP为为上边一个div的Top加上div本身的高度&#xff1a;2371<% Page Language…