Chris sells
Sells brothers Consulting
Abstract:Chris sells discusses non-type list resources and type resources, which are supported by the Microsoft. NET Framework. He defines these two resources and describes how to use them in your own applications.
Download the winforms02202003.exe sample file.
Suppose you want to set the background image of the form through loading bitmap from the file in the application:
public Form1() { ... // Load a file from the file system this.BackgroundImage = new Bitmap(@"C:\WINDOWS\Web\Wallpaper\Azul.jpg");}
The problem with this Code is that not all Microsoft Windows Installation instances have azul.jpg, and even those installation instances that do have this file may not be in the same location as the installation instance. Even if you deliver the image together with the application, the space-saving user may decide to delete it, which will cause your application to fail. The only safe way to ensure that an image or any file is associated with the code is to embed it as a resource and load it.
List Resources
Resources are added to the Assembly during compilation. For example, if you use the command line compiler, you can use the/resource switch to embed resources:
C:\>csc.exe myApp.cs /resource:c:\windows\web\wallpaper\Azul.jpg
The/resource switch embeds a file as a resource and uses the file name (no path) as the Resource Name When embedding the file. File embedded into the AssemblyListCentralized resources. An assembly list consists of a set of metadata that is part of the Assembly. A part of this metadata is the name and data associated with each embedded resource. When executing ildasm, you can see the list of assembly list Resources in the list section, as shown inFigure 1.
C:\>ildasm.exe myApp.exe
Figure 1. ildasm displays embedded Resources
You can enumerate the list of list resources like ildasm.System. reflection. AssemblyClassGetmanifestresourcenamesMethod:
using System.Reflection;...// Get this type's assemblyAssembly assem = this.GetType().Assembly;// Enumerate the assembly's manifest resourcesforeach( string resourceName in assem.GetManifestResourceNames() ) { MessageBox.Show(resourceName);}
Once you know the name of the configuration resource by enumerating the list resource or hardcoding a list resource you want, you canAssemblyClassGetmanifestresourcestreamThe method is as follows:
using System.IO;public Form1() { ... // Get this type's assembly Assembly 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 itself Stream stream = assem.GetManifestResourceStream("Azul.jpg"); // Load the bitmap from the stream this.BackgroundImage = new Bitmap(stream);}
Because resources may conflict like type names, it is best to use the resource's own "namespace" to embed the resource. This operation can be completed using the extended format of the/resource switch:
C:\>csc myApp.cs /resource:c:\...\azul.jpg,ResourcesApp.Azul.jpg
Note thatBackup Resource Name. The backup resource name allows you to provide any time nested name for the resource, regardless of the file name. It is a backup name set in the Assembly, suchFigure 2.
Figure 2. embed a resource with a backup name
The following is the updated resource loading code using the alternate name:
public Form1() { ... // Get this type's assembly Assembly assem = this.GetType().Assembly; // Load a resource with an alternate name Stream stream = assem.GetManifestResourceStream("ResourcesApp.Azul.jpg"); // Load the bitmap from the stream this.BackgroundImage = new Bitmap(stream);}
For convenience, if your resources and the classes that load resources happen to use the same namespace, you can pass the class type as the optional first parameterGetmanifestresourcestream:
namespace ResourcesApp { public class Form1 : Form { public Form1() { ... // Get this type's assembly Assembly 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 stream this.BackgroundImage = new Bitmap(stream); } ... }}
GetmanifestresourcestreamThe resource name will be written in the following format:
<namespace>.<fileName>
When loading some types (suchBitmapClass), the use of type and file name is also useful, so that you can avoid opening the stream by yourself by providing constructor:
Namespace resourcesapp {public class form1: FORM {public form1 (){... // get this type's Assembly assem = This. getType (). assembly; // load the bitmap directly from the manifest resources this. backgroundimage = new Bitmap (this. getType (), "azul.jpg ");}...}}
List resources in Visual Studio. NET
If (in most cases) You use Visual Studio? Net to develop and build the assembly, the method of embedding the list resource with the command line is not very attractive to you. In this case, you can add the resource to a Windows form project. This method will pass the appropriate command line parameters to the compiler.
To add resources to a project, right-click the project in Solution Explorer and selectAdd new itemAnd select the file you want to embed as a resource. The file will be copied to the project directory, but it will not be embedded. To embed a file as a resource, right-click the file and selectPropertiesAnd thenContent(Default) changedEmbedded Resource, SuchFigure 3.
Figure 3. Set the build action of the file to embedded Resource
This method of embedding resources allows Visual Studio. NET to create a backup resource name for you. The composition is similar to the following:
<defaultNamespace>.<folderName>.<fileName>
The default namespace part of the Resource Name is the default namespace of the project. It uses the Solution Explorer-> (right-click)->Properties->Common Properties->General->Default namespace. Because this is the same namespace obtained by the new class when the new class is generated, it is very convenient to load resources by using the type and some resource names. If the file happens to be located in the sub-folder of the project, you need to include the sub-folder in the folder name and replace the backslash with a dot. For example, a bitmap named azul.jpg is located in the foo \ bar folder under the project root. to load it, you need to do this:
// If this code called within the ResourcesApp.Form1 class,// and the file is \foo\bar\Azul.jpg,// will load ResourcesApp.foo.bar.Azul.jpgthis.BackgroundImage = new Bitmap(this.GetType(), "foo.bar.Azul.jpg");
Type resources available
Although the file has an extension, the list resources are embedded without type information. For example, if the name of the azul.jpg file is actually Azul. quuxBitmapClass, because this class determines its type (JPEG, PNG, GIF, etc.) by viewing the data itself ). This requires you to correctly map the type of each resource to the type of the object required to load the resource.
However, if you are willing to take one more step, you can use a type to mark the resource .. The. NET Framework supports a set of extended metadata for resources, including mime type information in two formats: Text Format and binary format. Both formats have built-in readers to get the right type of resources at runtime.
The text-based format is the XML format specific to the. NET Framework. It is called the resx (. resx file ). Without considering its XML basics, this format is not specifically designed for manual reading (the XML format is rarely like this ). However, Visual Studio. NET still provides a basic editor for the. resx file. To add a new. resx file to the Visual Studio. NET projectProjectSelectAdd new itemAnd then selectAssembly resource fileTemplate, suchFigure 4.
Figure 4. Add the. resx file to the Project
At the time of writing this article, even if the empty. resx file is 42 lines of XML, most of which are schema information. The schema allows any number of items in the. resx file. Each item has a name, value, comment, type, and MIME type.Figure 5The. resx file with two items is displayed, that is, the file namedMystringString and nameMyimage.
Figure 5. Simple. resx file in the data view of the designer
Unfortunately, only string items can be actually edited in the data view of the. resx editor. Any binary data must be manually input to XML (and can only be base64-encoded ). Therefore, directly using the. resx file is only useful for string resources (although indirect use will make the. resx file very useful for any type of data, we will discuss this later ).
FromSystem. ResourcesTheResxresourcereaderClass analyzes the XML file and exposes a group of named, typed values. To obtain a specific item, you need to find it:
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 resource this.Text = entry.Value.ToString(); } } }}
UseAdd new itemIn the dialog box that appears, adding the. resx file to the project will add the file to the project as an embedded resource, and compiling the project will result in. resx dataNested ResourcesEmbedded ("nested resources" are resources grouped into named containers ). The name of the container is the same as that of any file added as a resource, except that the. resx extension is not used and the. Resource extension is used. Assume that the default namespace of a project isResourcesappThe. resx file name isResources1.resx, The container name of the nested resource isResourcesapp. resources1.resx, SuchFigure 6As shown in ildasm.
Figure 6. Embedded. Resources file
The. Resources extension comes from the tool used to process the. resx file before embedding it as a resource. The Tool Name Is resgen.exe, which is used to "compile" The. resx XML format into binary format. You can manually compile the. resx file into a. Resources file, as shown below:
C:\> resgen.exe Resource1.resx
After compiling the. resx file into the. Resources file, you can useSystem. ResourcesIn the namespaceResourcereaderTo enumerate it:
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); }}
Besides the class name and input format,ResourcereaderClass usage andResxresourcereaderSame, and none of them allow random access to naming items.
Therefore, although you can. resx file. resources file, and use the/resource compiler command line switch to embed it, but it is much easier to directly let Visual Studio. net accept. resx file, and then compile it. the resources file is embedded in it, as shown in figure 4, Figure 5, and figure 6. Once the. Resources file is bound as a resource, you only need to perform the following two steps to access the resources in the. Resources file:
// 1. Load embedded .resources fileusing( Stream stream = assem.GetManifestResourceStream( this.GetType(), "Resource1.resources") ) { // 2. Find resource in .resources file using( ResourceReader reader = new ResourceReader(stream) ) { foreach( DictionaryEntry entry in reader ) { if( entry.Key.ToString() == "MyString" ) { // Set form caption from string resource this.Text = entry.Value.ToString(); } } }}
BecauseResourcereaderAndResxresourcereaderThese two steps are required to find specific resources. Therefore, the. NET Framework providesResourceManagerClass, which exposes a simpler use model.
Resource Manager
The ResourceManager class is also from the system. Resources namespace. It encapsulates resourcereader, which is used to enumerate resources during construction and expose them using their names:
public Form1() { ... // Get this type's assembly Assembly assem = this.GetType().Assembly; // Load the .resources file into the ResourceManager // Assumes a file named "Resource1.resx" as part of the project ResourceManager resman = new ResourceManager("ResourcesApp.Resource1", assem); // Set form caption from string resource this.Text = (string)resman.GetObject("MyString"); // The hard way this.Text = resman.GetString("MyString"); // The easy way}
The naming method used to find the. Resources file is the same as that used to name any other types of resources.Resource1.resourcesFile), only. ResourcesThe extension is assumed and cannot be included in the name. For convenience, if you happen to name a. resx file as a type name, The. Resources file and Assembly name are determined from the type:
// Use the type to determine resource name and assemblyResourceManager resman = new ResourceManager(this.GetType());
Once you have created a resource manager instance, you can useGetObjectTo find the nested resource by name. If you use the. resx file to process string resources, you can useGetstringMethod, this method will be executedSystem. StringType forced conversion.
Designer Resources
The lack of appropriate editors for. resx files makes it very difficult to use any other resource except string resources. You must not only manually write code to enter data at runtime, but also cannot see resource usage during design. For example, the background image of a form.
Fortunately, the designer once again helped us. If Visual Studio. NET Solution Explorer is enabled, selectShow all filesButton, you will see that each component (whether it is a form, control or simple component) has the corresponding. resx file. This is to maintain the association between resources and component attributes. This association is set in the property browser. For example, if you setBackgroundimageProperties, the form not only displays the background image in the designer, but also the. resx file of the form contains the corresponding items of the image. Similarly, if you setImageThe. resx file will also increase to include this resource. Both items can be found inFigure 7.
Figure 7. component. resx File
The. the resx file will be used as. the resources file is compiled and embedded, just as you have already added your own. the resx file is added to the project, which enables the resource to be used by components at runtime. In addition to the items in the. resx file of the component, the designer also adds the codeInitializecomponentIn order to load the resource manager of the component, and use the object obtained from the resource to fill in the properties of the component:
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"); ... } }}
Note:ResourceManagerThe object is constructed by the component type, which is used to construct the. Resources Resource Name of the component. Pay attention to the naming conventions used by the designer when naming resources. For attributes in component fields, the name format is:
<fieldName>.<propertyName>
For component attributes, the name format is:
$this.<propertyName>
If you want to add custom string properties for the component itself, you can do so, but make sure that the format is different from the name format generated by the designer.
Our location
The Microsoft. NET Framework supports two types of resources: untyped list resources and typed resources. By setting the build action of the file to embedded resource, you can set Visual Studio. net supports non-type list resources, and through. resx files (which can be custom files or backup storage of component resources) Support Type resources. The benefit of listing resources is that they can be directly edited in the IDE, while type resources require special work to be edited, but type access is available. Both types of resources have strict naming requirements, so be careful when writing method calls to load them.