Resource management in Visual Studio 2003/.Net 1.x has been drastically improved in Visual Studio 2005/.Net 2.0. I’ve mentioned in the past my partialness to strongly typed code (i.e. typed DataSets over regular datasets); I just don’t like seeing any strings in my code unless absolutely necessary…using types over strings lends itself to less error prone code. A pattern I find myself following for handling resources in .Net 1.x is writing a factory class to supply the correct ResourceManager class, then querying the supplied ResourceManager for the correct resource to retrieve…however this is string based, and you also have to specify an assembly to retrieve the resource settings for. Here’s a vastly simplified example:
using System;
using System.Configuration;
using cs = System.Configuration.ConfigurationSettings;
using System.Reflection;
using System.Resources;
namespace myNamespace
{
/// <summary>
/// Summary description for ResourceBroker.
/// </summary>
public class ResourceBroker
{
/// <summary>
/// Default constructor. Sets the resource file location to the default value in the configuration file.
/// </summary>
private ResourceBroker() { }
/// <summary>
/// Method to get the resource file for the application.
/// </summary>
/// <returns>The ResourceManager to use.</returns>
public static ResourceManager GetResources()
{
return GetResources(cs.AppSettings["ResourceFile"]);
}
/// <summary>
/// Method to get the resource file for the applicatioon from the specified resource file.
/// </summary>
/// <param name="resourceFile">The name of the resource file to retrieve resources from.</param>
/// <returns>The ResourceManager to use.</returns>
public static ResourceManager GetResources(string resourceFile)
{
return new ResourceManager(resourceFile, Assembly.GetExecutingAssembly());
}
}
}
You can then use the returned ResourceManager as follows:
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Resources;
using System.Windows.Forms;
namespace myNamespace
{
public class ContentSection : UserControl
{
... // various control decs
private ResourceManager rm = null;
private string myString = null;
public ContentSection()
{
// This call is required by the Windows.Forms Form Designer.
InitializeComponent();
rm = ResourceBroker.GetResources();
loadImages();
loadStrings();
}
private void loadImages()
{
this.myImageBox.Image = new Bitmap(this.GetType(), rm.GetString("myImage"));
}
private void loadStrings()
{
this.myString = rm.GetString("myString");
}
}
}
Notice the lookups are string based, and to retrieve an image resource you have to specify a Type in the Bitmap constructor. Not exactly intuitive, and not what I would consider “elegant” code. We’ve all written code like this in the past; enter VS2005 and .Net 2.0 where this is vastly simplified. The resource storage infrastructure has been beefed up to provide strongly typed support for the following:
- Strings.
- Images (in the form of Bitmaps).
- Icons (in the form of the Icon type).
- Text files (in the form of Streams).
- Audio files.
- Other types (in the form of Objects).
I’m assuming in future releases you’ll be able to specify your own types; the built in Resource designer in VS2005 beta 2 doesn’t support this, though I assume you could manually edit the .resx file that’s generated to support your own types. The new resource designer can be accessed by going to the project properties page and clicking on the Resources tab. After populating the values in the designer and building the project, a new folder called Properties will appear in the Solution outline in VS with a file called Resources.resx and a corresponding class file called Resources.Designer.cs. Again using our simple example from above where we only have 2 resources, this is the code that is generated by the designer:
namespace myNamespace.Properties {
/// <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.
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
internal class Resources {
private static global::System.Resources.ResourceManager resourceMan;
private static global::System.Globalization.CultureInfo resourceCulture;
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute
("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal Resources() {
}
/// <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 ((resourceMan == null)) {
global::System.Resources.ResourceManager temp =
new global::System.Resources.ResourceManager
("myNamespace.Properties.Resources", typeof(Resources).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 global::System.Globalization.CultureInfo Culture {
get {
return resourceCulture;
}
set {
resourceCulture = value;
}
}
/// <summary>
/// Looks up a localized string similar to myString.
/// </summary>
internal static string myString {
get {
return ResourceManager.GetString("myString", resourceCulture);
}
}
internal static System.Drawing.Bitmap myImage {
get {
return ((System.Drawing.Bitmap)(ResourceManager.GetObject("myImage", resourceCulture)));
}
}
}
}
As you can see, this is very similar to what we wrote by hand in the first example (basically just a static class wrapper around a ResourceManager object and the associated resources stored in it); Xml comments are auto generated for strings but not other types (useful for intellisense to snag the value of the string you’re looking for), and it also exposes it’s underlying ResourceManager as a property if you still need to query for resources the old way. Now imagine you have hundreds of resources…this is very time saving indeed. Querying for the resources you need is simple and less error prone; again using the example from above:
using System;
using System.Collections;
using System.ComponentModel;
using System.Drawing;
using System.Windows.Forms;
namespace myNamespace
{
public class ContentSection : UserControl
{
... // various control decs
private string myString = null;
public ContentSection()
{
loadImages();
loadStrings();
}
private void loadImages();
{
this.myImageBox.Image = Properties.Resources.myImage;
}
private void loadStrings();
{
this.myString = Properties.Resources.myString;
}
}
}
As is apparent, this is much more intuitive and less error prone. This could easily be extended to provide type names/type information for dynamically creating controls within your application; I can see many scenarios where this could be useful to match up types with the corresponding data they are meant to contain, i.e. generating an array of pictureBoxes based on names provided in the resource file, and also populating them with the data that matches up to this name…but that’s another post (and one I will be exploring soon as it would solve an immediate issue I’m having with one of the projects I’m working on).
So consider this a light weight introduction to Resource Management in Visual Studio 2005/.Net 2.0, and how it alleviates some of the tediousness of resources in previous versions of VS and .Net. In my poking around I also came across the Settings designer (for storing user specific application settings to be persisted across app execution, it also provides support for application wide settings as well which leads me to believe we are that much closer to getting away from the registry…permanently), I’ll also explore this in a future post as it looks very promising in my preliminary meanderings. I’ll probably write a new article with a sample application that uses both resource and settings management in the near future (been a while since I’ve actually written one!) as these are both great new features of .Net 2.0.
Note: VB.Net handles resource management via the My.Resources construct. This is documented on MSDN if you need more information about it. The documentation (as of beta 2) for handling resources in C# is virtually non-existent at this point (the only docs I could find on MSDN were for VB).
Posted
Jul 13 2005, 07:19 PM
by
Jayson Knight