Skip to main content
Email Password   helpLost your password?

Introduction

One of the great new features of the Visual Studio .NET 2005/2008 IDE is a custom tool called ResXFileCodeGenerator that is automatically associated with resources (*.resx files) every time they are added into a project. Whenever your project is rebuilt, a resource file is saved or a custom tool is run manually; the tool in question generates a managed class that exposes every resource you have in the *.resx file as a strongly typed static property. Now, any type of resource supported -- including images, icons, strings, etc. -- is a piece of cake to retrieve.

The two screenshots below illustrate the default properties of a resource file added to Visual Studio .NET 2005, and the Resource.Designer.cs source file which is dependent upon Resource.resx and is automatically generated by the ResXFileCodeGenerator custom tool.

Resource in Solution Explorer Resource properties

All of the properties exposed by the generated class are always static. The class exposes the following properties:

Resource editor

If your resource file contains only one string resource (like in the picture above), then the generated class would be like this:

/// <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.
// To add or remove a member, edit your .ResX file then rerun ResGen
// with the /str option, or rebuild your VS project.
[GeneratedCodeAttribute("Tools.StronglyTypedResourceBuilder", "2.0.0.0")]
[DebuggerNonUserCodeAttribute()]
[CompilerGeneratedAttribute()]
internal class Resource {
    private static ResourceManager resourceMan;
    private static CultureInfo resourceCulture;
    [SuppressMessageAttribute(
        "Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
    internal Resource() {
    }
    /// <summary>
    ///   Returns the cached ResourceManager instance used by this class.
    /// </summary>
    [EditorBrowsableAttribute(EditorBrowsableState.Advanced)]
    internal static ResourceManager ResourceManager {
        get {
            if (object.ReferenceEquals(resourceMan, null)) {
                ResourceManager temp =
                  new ResourceManager("MyApp.Resource", typeof(Resource).Assembly);
                resourceMan = temp;
            }
            return resourceMan;
        }
    }
    /// <summary>
    ///   Overrides the current thread's CurrentUICulture property for all
    ///   resource lookups using this strongly typed resource class.
    /// </summary>
    [EditorBrowsableAttribute(EditorBrowsableState.Advanced)]
    internal static CultureInfo Culture {
        get {
            return resourceCulture;
        }
        set {
            resourceCulture = value;
        }
    }
    /// <summary>
    ///   Looks up a localized string similar to Message text.
    /// </summary>
    internal static string Message {
        get {
            return ResourceManager.GetString("Message", resourceCulture);
        }
    }
}

Background

Despite the fact that the ResXFileCodeGenerator custom tool simplifies the process of resource access a lot, we can point out the following four major drawbacks:

The Extended Strongly Typed Resource Generator

With regard to the above described ResXFileCodeGenerator disadvantages, it was decided to develop an extended version of a strongly typed resource generator that remedies the deficiencies of the existing ResXFileCodeGenerator custom tool.

Using an extended version of the strongly typed resource generator is extremely straightforward, and does not differ from using the resource code generator shipped with Visual Studio .NET 2005 and 2008. The extended strongly typed resource generator is represented by two new custom tools:

First of all, you have to install and register the extended strongly typed resource generator (e.g., the ResXFileCodeGeneratorEx and InternalResXFileCodeGeneratorEx custom tools) on your box. Please remember that you must have administrator privileges to install and register new Visual Studio .NET custom tools. There are two ways to register the extended strongly typed resource generator on your computer:

After the extended strongly typed resource generator is installed on your box, you have to restart all running instances of Visual Studio .NET 2005 and 2008.

Resource properties with ResXFileCodeGeneratorEx custom tool

From this point on, you can use all the advantages of the extended strongly typed resource generator in your projects. You can manually specify ResXFileCodeGeneratorEx or InternalResXFileCodeGeneratorEx as a custom tool for your resource files, or you can adjust the default Visual Studio .NET 2005/2008 item templates.

Let's take the MyApp project as a real-world example, and add one more resource entry containing a formatted string. The most important step is to change the custom tool name to the extended strongly typed resource generator (ResXFileCodeGeneratorEx or InternalResXFileCodeGeneratorEx). Run the custom tool by either saving the resource file or running the custom tool manually. You have to right-click on the resource file in Visual Studio .NET and choose Run Custom Tool in the drop-down menu.

Resource editor with resources containing format strings

The ResXFileCodeGeneratorEx custom tool generates the resource wrapper class shown in the example below:

/// <summary>
/// A strongly-typed resource class, for looking up localized strings,
/// formatting them, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilderEx class via the
// ResXFileCodeGeneratorEx custom tool. To add or remove a member, edit your .ResX file
// then rerun the ResXFileCodeGeneratorEx custom tool or rebuild your VS.NET project.
// Copyright (c) Dmytro Kryvko 2006-2008 (http://dmytro.kryvko.googlepages.com/)
[GeneratedCodeAttribute
    ("DMKSoftware.CodeGenerators.Tools.StronglyTypedResourceBuilderEx", "2.3.0.0")]
[DebuggerNonUserCodeAttribute()]
[SuppressMessageAttribute
    ("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces")]
public partial class Resource {
    private static ResourceManager _resourceManager;
    private static object _internalSyncObject;
    private static CultureInfo _resourceCulture;
    [SuppressMessageAttribute
    ("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
    public Resource() {
    }
    /// <summary>
    /// Thread safe lock object used by this class.
    /// </summary>
    public static object InternalSyncObject {
        get {
            if (object.ReferenceEquals(_internalSyncObject, null)) {
                Interlocked.CompareExchange
        (ref _internalSyncObject, new object(), null);
            }
            return _internalSyncObject;
        }
    }
    /// <summary>
    /// Returns the cached ResourceManager instance used by this class.
    /// </summary>
    [EditorBrowsableAttribute(EditorBrowsableState.Advanced)]
    public static ResourceManager ResourceManager {
        get {
            if (object.ReferenceEquals(_resourceManager, null)) {
                Monitor.Enter(InternalSyncObject);
                try {
                    if (object.ReferenceEquals(_resourceManager, null)) {
                        Interlocked.Exchange(ref _resourceManager,
                            new ResourceManager("MyApp.Resource",
                    typeof(Resource).Assembly));
                    }
                }
                finally {
                    Monitor.Exit(InternalSyncObject);
                }
            }
            return _resourceManager;
        }
    }
    /// <summary>
    /// Overrides the current thread's CurrentUICulture property for all
    /// resource lookups using this strongly typed resource class.
    /// </summary>
    [EditorBrowsableAttribute(EditorBrowsableState.Advanced)]
    public static CultureInfo Culture {
        get {
            return _resourceCulture;
        }
        set {
            _resourceCulture = value;
        }
    }
    /// <summary>
    /// Looks up a localized string similar to 'Hello, {0}!'.
    /// </summary>
    public static string Hello {
        get {
            return ResourceManager.GetString(ResourceNames.Hello, _resourceCulture);
        }
    }
    /// <summary>
    /// Looks up a localized string similar to 'Message text'.
    /// </summary>
    public static string Message {
        get {
            return ResourceManager.GetString
        (ResourceNames.Message, _resourceCulture);
        }
    }
    /// <summary>
    /// Formats a localized string similar to 'Hello, {0}!'.
    /// </summary>
    /// <param name="arg0">An object (0) to format.</param>
    /// <returns>A copy of format string in which the format
    /// items have been replaced by the String equivalent of
    /// the corresponding instances of Object in arguments.</returns>
    public static string HelloFormat(object arg0) {
        return string.Format(_resourceCulture, Hello, arg0);
    }
    /// <summary>
    /// Lists all the resource names as constant string fields.
    /// </summary>
    public class ResourceNames {
        /// <summary>
        /// Stores the resource name 'Hello'.
        /// </summary>
        public const string Hello = "Hello";
        /// <summary>
        /// Stores the resource name 'Message'.
        /// </summary>
        public const string Message = "Message";
    }
}

As you can see, the generated class is public, which allows you to make shared resources between assemblies. However, the major difference is that an additional method called HelloFormat has been added. This method is a result of the analysis and validation of the Hello resource entry string value. The extended strongly typed resource generator automatically determines whether a resource string value is a valid .NET Framework format string and generates code correspondingly.

The name of a format method is always generated in the following way: the resource property plus the Format suffix. The number of arguments is calculated automatically, and equals the number of parameters that the String.Format() method expects. On the other hand, there is still a possibility to get the format string using the exposed Hello property. As it was mentioned above, the extended strongly typed resource generator performs format string validation. For example, by mistake, you could write an invalid format string like: Hello, {{0}. The (internal) ResXFileCodeGeneratorEx custom tool will resolve the invalid format and will show you a warning about that. In this particular case, the format method will not be generated, but the resource access property will still remain in the generated class.

Resource generation warning

Another set of small improvements over the standard Visual Studio resource wrapper generator:

Generation of public resource class wrappers suits almost everybody, however, some folks still want to have the ability to generate internal resource wrappers. Therefore, version 2.1 brings in the InternalResXFileCodeGeneratorEx Visual Studio .NET custom tool, generating strongly typed internal resource wrappers. The output of InternalResXFileCodeGeneratorEx is shown in the example below:

/// <summary>
/// A strongly-typed resource class, for looking up localized strings,
/// formatting them, etc.
/// </summary>
// This class was auto-generated by the StronglyTypedResourceBuilderEx class via the
// InternalResXFileCodeGeneratorEx custom tool.
// To add or remove a member, edit your .ResX file
// then rerun the InternalResXFileCodeGeneratorEx custom tool or
// rebuild your VS.NET project.
// Copyright (c) Dmytro Kryvko 2006-2008 (http://dmytro.kryvko.googlepages.com/)
[GeneratedCodeAttribute
    ("DMKSoftware.CodeGenerators.Tools.StronglyTypedResourceBuilderEx", "2.3.0.0")]
[DebuggerNonUserCodeAttribute()]
[SuppressMessageAttribute
    ("Microsoft.Naming", "CA1724:TypeNamesShouldNotMatchNamespaces")]
internal partial class Resource {
    private static ResourceManager _resourceManager;
    private static object _internalSyncObject;
    private static CultureInfo _resourceCulture;
    [SuppressMessageAttribute
    ("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
    internal Resource() {
    }
    /// <summary>
    /// Thread safe lock object used by this class.
    /// </summary>
    internal static object InternalSyncObject {
        get {
            if (object.ReferenceEquals(_internalSyncObject, null)) {
                Interlocked.CompareExchange
        (ref _internalSyncObject, new object(), null);
            }
            return _internalSyncObject;
        }
    }
    /// <summary>
    /// Returns the cached ResourceManager instance used by this class.
    /// </summary>
    [EditorBrowsableAttribute(EditorBrowsableState.Advanced)]
    internal static ResourceManager ResourceManager {
        get {
            if (object.ReferenceEquals(_resourceManager, null)) {
                Monitor.Enter(InternalSyncObject);
                try {
                    if (object.ReferenceEquals(_resourceManager, null)) {
                        Interlocked.Exchange(ref _resourceManager,
                            new ResourceManager("MyApp.Resource",
                    typeof(Resource).Assembly));
                    }
                }
                finally {
                    Monitor.Exit(InternalSyncObject);
                }
            }
            return _resourceManager;
        }
    }
    /// <summary>
    /// Overrides the current thread's CurrentUICulture property for all
    /// resource lookups using this strongly typed resource class.
    /// </summary>
    [EditorBrowsableAttribute(EditorBrowsableState.Advanced)]
    internal static CultureInfo Culture {
        get {
            return _resourceCulture;
        }
        set {
            _resourceCulture = value;
        }
    }
    /// <summary>
    /// Looks up a localized string similar to 'Hello, {0}!'.
    /// </summary>
    internal static string Hello {
        get {
            return ResourceManager.GetString(ResourceNames.Hello, _resourceCulture);
        }
    }
    /// <summary>
    /// Looks up a localized string similar to 'Message text'.
    /// </summary>
    internal static string Message {
        get {
            return ResourceManager.GetString(ResourceNames.Message, _resourceCulture);
        }
    }
    /// <summary>
    /// Formats a localized string similar to 'Hello, {0}!'.
    /// </summary>
    /// <param name="arg0">An object (0) to format.</param>
    /// <returns>A copy of format string in which the format
    /// items have been replaced by the String equivalent of
    /// the corresponding instances of Object in arguments.</returns>
    internal static string HelloFormat(object arg0) {
        return string.Format(_resourceCulture, Hello, arg0);
    }
    /// <summary>
    /// Lists all the resource names as constant string fields.
    /// </summary>
    internal class ResourceNames {
        /// <summary>
        /// Stores the resource name 'Hello'.
        /// </summary>
        internal const string Hello = "Hello";
        /// <summary>
        /// Stores the resource name 'Message'.
        /// </summary>
        internal const string Message = "Message";
    }
}

History

You must Sign In to use this message board.
 
 
Per page   
 FirstPrevNext
GeneralMicrosoft's VS2008 PublicResXFileCodeGenerator Pin
wilson mar
4:00 16 Sep '09  
GeneralRe: Microsoft's VS2008 PublicResXFileCodeGenerator Pin
SimmoTech
4:17 8 Nov '09  
GeneralVisual Studio 2010/.NET 4.0 support Pin
wilson mar
3:47 16 Sep '09  
GeneralINotifyPropertyChanged Pin
correttore_automatico
23:12 23 Jul '09  
GeneralRe: INotifyPropertyChanged Pin
bugmenot1234
10:42 21 Aug '09  
GeneralInstallation Error Code 2869 Pin
correttore_automatico
22:52 23 Jul '09  
GeneralObfuscationAttribute not implemented in mscorlib in Silverlight 2 Pin
r2musings
19:24 19 Mar '09  
GeneralRe: ObfuscationAttribute not implemented in mscorlib in Silverlight 2 Pin
Dmytro Kryvko
19:26 19 Mar '09  
GeneralRe: ObfuscationAttribute not implemented in mscorlib in Silverlight 2 Pin
pfennigfuchser
0:42 17 Apr '09  
Generalcool and very useful Pin
Gabriela Baba
0:14 17 Feb '09  
AnswerRe: cool and very useful Pin
Dmytro Kryvko
10:58 20 Feb '09  
Generalextending code generation question Pin
bjohansen
18:46 21 Jan '09  
AnswerRe: extending code generation question Pin
Dmytro Kryvko
10:21 9 Feb '09  
GeneralObfuscating Pin
FriedhelmEichin
21:56 6 Nov '08  
GeneralRe: Obfuscating Pin
Dmytro Kryvko
19:05 9 Nov '08  
NewsRe: Obfuscating Pin
Dmytro Kryvko
10:56 20 Feb '09  
QuestionDebugging this project Pin
DrEVOware
2:01 15 Oct '08  
AnswerRe: Debugging this project Pin
Dmytro Kryvko
12:04 18 Oct '08  
GeneralRe: Debugging this project Pin
DrEVOware
23:48 20 Oct '08  
GeneralRe: Debugging this project Pin
Dmytro Kryvko
7:42 27 Oct '08  
GeneralRe: Debugging this project Pin
DrEVOware
23:01 11 Nov '08  
QuestionUse with Web Site Project System Pin
lookaround
4:47 18 Sep '08  
AnswerRe: Use with Web Site Project System Pin
Dmytro Kryvko
21:47 24 Sep '08  
GeneralRe: Use with Web Site Project System Pin
lookaround
21:59 24 Sep '08  
GeneralIncluding in pre-build event Pin
Sanjay Pais
4:40 20 Aug '08  


Last Updated 31 Mar 2009 | Advertise | Privacy | Terms of Use | Copyright © CodeProject, 1999-2009