Microsoft .NET Framework Resource Basics

 

Chris Sells
Sells Brothers Consulting

February 3, 2003

Summary: Chris Sells discusses untyped manifest resources and typed resources, which are the two kinds of resources supported by the Microsoft .NET Framework. He defines both resources and shows you how to use them in your own applications. (12 printed pages)

Download the winforms02202003.exe sample file.

Imagine setting the background image of a form in your application by loading a bitmap from a file:

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 installations of Microsoft Windows® will have Azul.jpg, and even those that do may not have it in the same place. Even if you shipped this picture with your application, a space-conscious user may decide to remove it, causing your application to fault. The only safe way to make sure that the picture, or any file, stays with code is to embed it and load it as a resource.

Manifest Resources

Resources are added to an assembly at compile-time. For example, if you were using the command-line compiler, you can embed a resource using the /resource switch:

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

The /resource switch embeds a file as a resource, using the name of the file (without the path) as the name of the resource. The file gets embedded into the assembly's set of manifest resources. The manifest of an assembly is composed of a set of metadata that's part of the assembly. Part of that metadata is the name and data associated with each embedded resource. You can see a list of an assembly's manifest resources in the Manifest section when executing ildasm as shown in Figure 1.

C:\>ildasm.exe myApp.exe

Figure 1. ildasm showing an embedded resource

You can enumerate the list of manifest resources, as ildasm is doing, using the GetManifestResourceNames method of the System.Reflection.Assembly class:

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);
}

Once you know the name of a manifest resource, either by enumerating them or by hard-coding the one you want, it can be loaded as a raw stream of bytes through the GetManifestResourceStream method of the Assembly class 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 can collide just as type names can, it's a good idea to embed resources with their own "namespace," which can be accomplished using an extended form of the /resource switch:

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

Notice that use of an alternate resource name after the comma of the file name to be embedded. The alternate resource name allows you to provide arbitrarily period-nested names for resources, regardless of the name of the file. It's the alternate name that makes it into the assembly, as shown in Figure 2.

Figure 2. An embedded resource with an alternate name

The following is the updated code to load a resource 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);
}

As a further convenience, if you happen to use the same namespace for both the resource and the class loading the resource, you can pass the type of the class as the optional first argument to GetManifestResourceStream:

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);
    }
    ...
  }
}

GetManifestResourceStream will compose the resource name using the following format:

<namespace>.<fileName>

The use of a type and a file name is also useful for loading certain types, like the Bitmap class, which provide a constructor that saves you from opening the stream yourself:

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");
    }
    ...
  }
}

Manifest Resources in Visual Studio .NET

In case, like most of the world, you use Visual Studio®.NET to develop and build your assemblies, the command line method of embedding manifest resources is unlikely to be very attractive to you. In that case, you can add a resource to a Windows Forms project, which will pass the appropriate command line arguments to the compiler for you.

To add a resource to a project, right-click on your project in the Solution Explorer, choose Add New Item and choose the file you'd like to embed as a resource. The file will be copied into your project's directory, but will still not be embedded. To cause the file to be embedded as a resource, right-click on the file and choose Properties, then change the Build Action from Content (the default) to Embedded Resource, as shown in Figure 3.

Figure 3. Setting a file's Build Action to Embedded Resource

This method of embedding a resource causes Visual Studio .NET to create an alternate resource name for you composed like so:

<defaultNamespace>.<folderName>.<fileName>

The default namespace portion of the resource name is the default namespace of the project itself, as set through the Solution Explorer-><projectName>(right-click)->Properties->Common Properties->General->Default Namespace. Since this is the same namespace that new classes get when being generated, this makes loading resources using a type and a partial resource name convenient. If the file happens to be in a sub-folder of your project, you'll need to include that in the folder name, replacing the back-slashes with dots. For example, a bitmap named Azul.jpg in the foo\bar folder under the root of the project will need to be loaded like so:

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

Typed Resources

Manifest resources are embedded with no type information, despite the extension on the file. For example, if the name of the Azul.jpg file was actually Azul.quux that would make no difference to the Bitmap class, which is looking at the data itself for the type (JPEG, PNG, GIF, and so on). It's up to you to properly map the type of each resource to the type of the object needed to load that resource.

However, you can tag your resources with a type if you're willing to take an extra step. The .NET Framework supports an extended set of metadata for a resource that includes MIME type information in two formats, one text and one binary. Both of these formats have readers built-in so that you can pull out the properly typed resources at runtime.

The text-based format is a .NET Framework-specific XML format called ResX (.resx files). In spite of its XML basis, this format is not meant for humans (as few XML formats are). However, Visual Studio .NET does provide a rudimentary editor for .resx files. To add a new .resx file to your Visual Studio .NET project, choose Add New Item from the Project menu and pick the Assembly Resource File template, as shown in Figure 4.

Figure 4. Adding a .resx file to a project

As of this writing, even an empty .resx file is 42 lines of XML, most of which is the schema information. The schema allows for any number of entries in the .resx file, each of which has a name, value, comment, type, and MIME type. Figure 5 shows a .resx file with two entries, a string named MyString and an image named MyImage.

Figure 5. A simple .resx file in the Data view of the Designer

Unfortunately, only the string entry could actually be edited from within the Data view of the .resx editor. Any binary data needs to be entered by hand directly into the XML (and base64-encoded at that). For this reason, direct use of .resx files is only useful for string resources (although indirect usage makes .resx files very useful for any kind of data, as we'll see later).

The ResXResourceReader class from the System.Resources namespace parses the XML file and exposes a set of named, typed values. Pulling out a specific entry requires finding 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();
      }
    }
  }
}

Using the Add New Item dialog to add a .resx file to a project causes it to be added as an Embedded Resource, and building the project causes the .resx data to be embedded as nested resources, that is resources grouped into a named container. The name of the container is the same as any file added as a resource, except that instead of a .resx extension, there's a .resource extension. Assuming a project's default namespace of ResourcesApp and a .resx file named Resources1.resx, the container of nested resources is named ResourcesApp.Resources1.resx as shown in ildasm in Figure 6.

Figure 6. An embedded .resources file

The .resources extension comes from the tool that Visual Studio .NET uses on the .resx file before embedding it as a resource. The name of the tool is resgen.exe and it's used to "compile" the .resx XML format to a binary format. You can compile a .resx file into a .resources file manually like so:

C:\> resgen.exe Resource1.resx

Once you've compiled a .resx file into a .resources file, you can enumerate it using the ResourceReader from the System.Resources namespace:

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);
  }
}

Other than the name of the class and the input format, the ResourceReader class usage is identical to ResXResourceReader, including the lack of random access for named entries.

So, while you could turn a .resx file into a .resources file and embed it using the /resource compiler command line switch, it's much easier to simply let Visual Studio .NET take an .resx file marked as an Embedded Resources in your project, compile it into a .resources file, and embed it for you, as shown in Figure 4, Figure 5, and Figure 6. Once a .resources file is bundled as a resource, getting to the resource in the .resources file is a two-step process:

// 1. Load embedded .resources file
using( 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();
      }
    }
  }
}

Because both the ResourceReader and the ResXResourceReader require this two-step process to find a specific resource, the .NET Framework provides the ResourceManager class, which exposes a simpler usage model.

Resource Manager

The ResourceManager class, also from the System.Resources namespace, wraps a ResourceReader, enumerating the resources at construction time and exposing them using their name:

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 used to find the .resources file is the same as naming any other kind of resource (notice the use of the project's default namespace appended to the Resource1.resources" file) except that the .resources extension is assumed and cannot be included in the name. As a further convenience, if you happen to name a .resx file with the name of a type, the name of the .resources file and the assembly will be determined from the type:

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

Once you've created an instance of the resource manager, you can pull out nested resources by name using the GetObject method and casting to the appropriate type. If you're using the .resx file for string resources, you'll want to use the GetString method instead, which performs the cast to the System.String type for you.

Designer Resources

The lack of a decent editor for .resx files makes their use for anything but string resources pretty difficult. Not only do you have to write the code manually to bring the data in at runtime, but the use of resources can't be seen at design-time; for example, the background image of a form.

Luckily, the Designer comes to our rescue once again here. If you open the Visual Studio .NET Solution Explorer and select the Show All Files button, you'll see that each component, whether it's a form, a control, or a simple component, has a corresponding .resx file. This is to keep resources associated with properties of the component, as set in the Property Browser. For example, if you set the BackgroundImage property of a form, not only will the form show the background image in the Designer, but also the form's .resx file will contain an entry for that image. Likewise, if you set the Image property of a PictureBox control on the same form, the .resx file will have grown to include that resource as well. Both of these entries can be seen in Figure 7.

Figure 7. A component's .resx file

Each component's .resx file will be compiled and embedded as a .resources file, just as if you'd added your own .resx file to your project, making the resources available to the component at runtime. In addition to the entries in the component's .resx file, the Designer will add the code to InitializeComponent to load a resource manager for the component and populate the component's properties using the objects pulled from the resources:

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");

      ...
    }
  }
}

Notice that the ResourceManager object is constructed using the type of the component, which is used to construct the .resources resource name for the component. Notice also the naming convention used by the Designer to name resources. For properties on fields of the component, the format of the name is:

<fieldName>.<propertyName>

For properties on the component itself, the format of the name is:

$this.<propertyName>

If you'd like to add custom string properties for use by the component itself, you can do so, but make sure to stay away from the format of the Designer-generated names.

Where Are We?

The Microsoft .NET Framework supports two kinds of resources—untyped manifest resources and typed resources. Visual Studio .NET supports untyped manifest resources by setting a file's Build Action to Embedded Resource, and supports typed resources through .resx files, either custom files or as the backing store for component resources. Manifest resources have the benefit that they're directly editable in the IDE, while typed resources require extraordinary effort to edit, but provide typed access. Both resource types have some stringent naming requirements, so be extra careful when you compose method calls to load them.

Note This material is excerpted from the forthcoming Addison-Wesley title: Windows Forms Programming in C# by Chris Sells (0321116208). Please note the material presented here is an initial DRAFT of what will appear in the published book.

Chris Sells in an independent consultant, speaker and author specializing in distributed applications in .NET and COM. He's written several books and is currently working on Windows Forms for C# and VB.NET Programmers and Mastering Visual Studio .NET. In his free time, Chris hosts various conferences, directs the Genghis source-available project, plays with Rotor and, in general, makes a pest of himself at Microsoft design reviews. More information about Chris, and his various projects, is available at http://www.sellsbrothers.com.