Resources


Resources such as pictures or string tables can be put into resource files or satellite assemblies. Such resources can be very helpful when localizing applications, and .NET has built-in support to search for localized resources.

Before you see how to use resources to localize applications, the next sections discuss how resources can be created and read without looking at language aspects.

Creating Resource Files

Resource files can contain such things as pictures and string tables. A resource file is created by using either a normal text file or a .resX file that utilizes XML. This section starts with a simple text file.

A resource that embeds a string table can be created by using a normal text file. The text file just assigns strings to keys. The key is the name that can be used from a program to get the value. Spaces are allowed in both keys and values.

This example shows a simple string table in the file strings.txt:

  Title = Professional C# Chapter = Localization Author = Christian Nagel Publisher = Wrox Press 

Tip 

Saving text files with Unicode characters, you must save the file with the proper encoding. Select the Unicode encoding with the Save dialog.

Resource File Generator

The Resource File Generator (Resgen.exe) utility can be used to create a resource file out of strings.txt. Typing

 resgen strings.txt 

creates the file strings.resources. The resulting resource file can either be added to an assembly as an external file or embedded into the DLL or EXE. Resgen also supports the creation of XML-based .resX resource files. One easy way to build an XML file is by using Resgen itself:

 resgen strings.txt strings.resX 

This command creates the XML resource file strings.resX. You look at how to work with XML resource files in the section “Localization Example with Visual Studio” later in this chapter.

In .NET 2.0, Resgen supports strongly typed resources. A strongly typed resource is represented by a class that accesses the resource. The class can be created with the /str option of the Resgen utility:

 resgen /str:C#,DemoNamespace,DemoResource,DemoResource.cs strings.resX 

With the option /str, the language, namespace, class name, and the file name for the source code are defined in that order.

The Resgen utility does not support adding pictures. With the .NET Framework SDK samples, you get a ResXGen sample with the tutorials. With ResXGen it is possible to reference pictures in a .resX file. Adding pictures can also be done programmatically by using the ResourceWriter or ResXResourceWriter classes, as you see next.

ResourceWriter

Instead of using the Resgen utility to build resource files, a simple program can be written. The class ResourceWriter from the namespace System.Resources can be used to write binary resource files; ResXResourceWriter writes XML-based resource files. Both of these classes support pictures and any other object that is serializable. When you use the class ResXResourceWriter, the assembly System.Windows.Forms must be referenced.

In the following code example, you create a ResXResourceWriter object, rw, using a constructor with the file name Demo.resx. After creating an instance, you can add a number of resources of up to 2GB in total size using the AddResource() method of the ResXResourceWriter class. The first argument of AddResource() specifies the name of the resource and the second argument specifies the value. A picture resource can be added using an instance of the Image class. To use the Image class, you have to reference the assembly System.Drawing. You also add the using directive to open the namespace System.Drawing.

Create an Image object by opening the file logo.gif. You will have to copy the picture to the directory of the executable or specify the full path to the picture in the method argument of Image.ToFile(). The using statement specifies that the image resource should automatically be disposed at the end of the using block. Additional simple string resources are added to the ResXResourceWriter object. The Close() method of the ResXResourceWriter class automatically calls ResXResourceWriter .Generate() to finally write the resources to the file Demo.resx:

  using System; using System.Resources; using System.Drawing; class Program {    static void Main()    {       ResXResourceWriter rw = new ResXResourceWriter("Demo.resx");       using (Image image = Image.FromFile("logo.gif"))       {          rw.AddResource("WroxLogo", image);          rw.AddResource("Title", "Professional C#");          rw.AddResource("Chapter", "Localization");          rw.AddResource("Author", "Christian Nagel");          rw.AddResource("Publisher", "Wrox Press");          rw.Close();       }    } } 

Starting this small program creates the resource file Demo.resx that embeds the image logo.gif. The resources will now be used in the next example with a Windows application.

Using Resource Files

You can add resource files to assemblies with the command-line C# compiler csc.exe using the /resource option, or directly with Visual Studio 2005. To see how resource files can be used with Visual Studio 2005, create a C# Windows application and name it ResourceDemo.

Use the context menu of the Solution Explorer (Add image from book Add Existing Item) to add the previously created resource file Demo.resx to this project. By default, BuildAction of this resource is set to Embedded Resource so that this resource is embedded into the output assembly (see Figure 20-9).

image from book
Figure 20-9

After building the project, you can check the generated assembly with ildasm to see the attribute .mresource in the manifest (see Figure 20-10). .mresource declares the name for the resource in the assembly. If .mresource is declared as public (as in the example), the resource is exported from the assembly and can be used from classes in other assemblies. .mresource private means that the resource is not exported and is only available within the assembly.

image from book
Figure 20-10

When you add resources to the assembly using Visual Studio 2005, the resource is always public, as shown in Figure 20-10. If the assembly generation tool is used to create assemblies, you can use command-line options to differentiate between adding public and private resources. The option /embed:demo.resources,Y adds the resource as public, whereas /embed:demo.resources,N adds the resource as private.

Important 

If the assembly was generated using Visual Studio 2005, you can change the visibility of the resources later. Use ilasm and select File image from book Dump to open the assembly and generate an MSIL source file. You can change the MSIL code with a text editor. Using the text editor, you can change .mresource public to .mresource private. Using the tool ilasm, you can then regenerate the assembly with the MSIL source code: ilasm/exe ResourceDemo.il.

In your Windows application, you add some text boxes and a picture by dropping Windows Forms elements from the toolbox into the Designer. The values from the resources will be displayed in these Windows Forms elements. Change the Text and Name properties of the text boxes and the labels to the values that you can see in the following code. The name property of the PictureBox control is changed to logo. Figure 20-11 shows the final form in the Forms Designer. The PictureBox control is shown as a rectangle without grid in the upper-left corner.

image from book
Figure 20-11

To access the embedded resource, use the ResourceManager class from the System.Resources namespace. You can pass the assembly that has the resources as an argument to the constructor of the ResourceManager class. In this example, the resources are embedded in the executing assembly, so pass the result of Assembly.GetExecutingAssembly() as the second argument. The first argument is the root name of the resources. The root name consists of the namespace, with the name of the resource file but without the resources extension. As you saw earlier, ildasm shows the name. All you have to do is remove the file extension resources from the name shown. You can also get the name programmatically using the GetManifestResourceNames() method of the System.Reflection.Assembly class:

  using System.Reflection; using System.Resources; //...    partial class ResourceDemoForm : Form    {       private System.Resources.ResourceManager rm;       public ResourceDemoForm()       {          InitializeComponent();          Assembly assembly = Assembly.GetExecutingAssembly();          rm = new ResourceManager("ResourceDemo.Demo", assembly); 

Using the ResourceManager instance rm, you can get all the resources by specifying the key to the methods GetObject() and GetString():

     logo.Image = (Image)rm.GetObject("WroxLogo");    textTitle.Text = rm.GetString("Title");    textChapter.Text = rm.GetString("Chapter");    textAuthor.Text = rm.GetString("Author");    textPublisher.Text = rm.GetString("Publisher"); } 

When you run the code, you can see the string and picture resources (see Figure 20-12).

image from book
Figure 20-12

.NET 2.0 has a new feature of strongly typed resources, as mentioned earlier. With strongly typed resources, the code written earlier in the constructor of the class ResourceDemoForm can be simplified; there’s no need to instantiate the ResourceManager and access the resources using indexers. Instead the names of the resources are accessed with properties:

 public ResourceDemoForm() {    InitializeComponent();        pictureLogo.Image = Demo.WroxLogo;    textTitle.Text = Demo.Title;    textChapter.Text = Demo.Chapter;    textAuthor.Text = Demo.Author;    textPublisher.Text = Demo.Publisher; }

To create a strongly typed resource, the Custom Tool property of the XML-based resource file must be set to ResXFileCodeGenerator. By setting this option, the class Demo (it has the same name as the resource) is created. This class has static properties for all the resources to offer a strongly typed resource name. With the implementation of the static properties, a ResourceManager object is used that is instantiated on first access and then cached:

 /// <summary> ///    A strongly-typed resource class, for looking up localized strings, etc. /// </summary> // This class was auto-generated by the StronglyTypedResourceBuilder // class via a tool like ResGen or Visual Studio.NET. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. [global::System.CodeDom.Compiler.GeneratedCodeAttribute(       "System.Resources.Tools.StronglyTypedResourceBuilder",       "2.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] internal class Demo {    private static global::System.Resources.ResourceManager resourceMan;    private static global::System.Globalization.CultureInfo resourceCulture;    [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute(          "Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]    internal Demo() {    }    /// <summary>    ///    Returns the cached ResourceManager instance used by this class.    /// </summary>    [global::System.ComponentModel.EditorBrowsableAttribute(          global::System.ComponentModel.EditorBrowsableState.Advanced)]    internal static global::System.Resources.ResourceManager          ResourceManager {       get {          if (object.ReferenceEquals(resourceMan, null)) {             global::System.Resources.ResourceManager temp =                   new global::System.Resources.ResourceManager(                   "ResourceDemo.Demo", typeof(Demo).Assembly);             resourceMan = temp;          }          return resourceMan;       }    }    /// <summary>    ///    Overrides the current thread's CurrentUICulture property for all    ///    resource lookups using this strongly typed resource class.    /// </summary>    [global::System.ComponentModel.EditorBrowsableAttribute(          global::System.ComponentModel.EditorBrowsableState.Advanced)]    internal static System.Globalization.CultureInfo Culture {       get {          return resourceCulture;       }       set {          resourceCulture = value;       }    }    /// <summary>    ///    Looks up a localized string similar to "Christian Nagel".    /// </summary>    internal static string Author {       get {          return ResourceManager.GetString("Author", resourceCulture);       }    }    /// <summary>    ///    Looks up a localized string similar to "Localization".    /// </summary>    internal static string Chapter {       get {          return ResourceManager.GetString("Chapter", resourceCulture);       }    }        /// <summary>    ///    Looks up a localized string similar to "Wrox Press".    /// </summary>    internal static string Publisher {       get {          return ResourceManager.GetString("Publisher", resourceCulture);       }    }    /// <summary>    ///    Looks up a localized string similar to "Professional C#".    /// </summary>    internal static string Title {       get {          return ResourceManager.GetString("Title", resourceCulture);       }    }    internal static System.Drawing.Bitmap WroxLogo {       get {          return ((System.Drawing.Bitmap)(ResourceManager.GetObject(                "WroxLogo", rescourceCulture)));       }    } }

The System.Resources Namespace

Before moving on to the next example, this section concludes with a review of the classes contained in the System.Resources namespace that deal with resources:

  • The ResourceManager class can be used to get resources for the current culture from assemblies or resource files. Using the ResourceManager, you can also get a ResourceSet for a particular culture.

  • A ResourceSet represents the resources for a particular culture. When a ResourceSet instance is created it enumerates over a class, implementing the interface IResourceReader, and stores all resources in a Hashtable.

  • The interface IResourceReader is used from the ResourceSet to enumerate resources. The class ResourceReader implements this interface.

  • The class ResourceWriter is used to create a resource file. ResourceWriter implements the interface IResourceWriter.

  • ResXResourceSet, ResXResourceReader, and ResXResourceWriter are similar to ResourceSet, ResourceReader, and ResourceWriter; however, they are used to create an XML-based resource file .resX instead of a binary file. You can use ResXFileRef to make a link to a resource instead of embedding it inside an XML file.




Professional C# 2005 with .NET 3.0
Professional C# 2005 with .NET 3.0
ISBN: 470124725
EAN: N/A
Year: 2007
Pages: 427

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net