Localizing the Property Grid
The Property Grid is quite simply my all time
favourite control - there's just so much there it would
take a book to do justice to all the capabilities. One
that isn't really a property grid facility, but is
manifest within the grid is the use of localized data,
such as the category and description that is shown for a
given property. This article will describe how to roll
your own localized UI.
How it's Done
The property grid reads the
Description and
Category attributes in order to determine what's
shown on screen, so all we need to do is hijack these
and provide our own classes which return localized text.
The method is different for each class, so I'll describe
each in turn. The basics are :-
- Derive your own class from the xxxAttribute class
- Override something to get your localization code
called
- Attribute code with your new attribute.
Localizing Descriptions
Here you need to create a subclass of
System.ComponentModel.DescriptionAttribute, and then
override the Description property...
|
using System;
using System.ComponentModel;
namespace LocalizedPropertyGrid
{
[AttributeUsage(AttributeTargets.All,AllowMultiple=false,Inherited=true)]
public class SRDescriptionAttribute : DescriptionAttribute
{
public SRDescriptionAttribute ( string text ) : base ( text )
{
_localized = false ;
}
public override string Description
{
get
{
if ( !_localized )
{
_localized = true ;
this.DescriptionValue = SR.GetString ( this.DescriptionValue ) ;
}
return base.Description ;
}
}
private bool _localized ;
}
}
|
This class can then be used in place of the regular
Description attribute.
Note: This class caches the
localized version of the attribute, so if you happen to
permit the UI culture to change without re-running the
application then you should remove this caching and
always call SR.GetString() from within the Description
property getter.
Localizing Categories
This one's a little simpler as there's a method on
System.ComponentModel.CategoryAttribute called GetLocalizedString which is all you need to override...
|
using System;
using System.ComponentModel;
namespace LocalizedPropertyGrid
{
[AttributeUsage(AttributeTargets.All,AllowMultiple=false,Inherited=true)]
public class SRCategoryAttribute : CategoryAttribute
{
public SRCategoryAttribute ( string name ) : base ( name ) { }
protected override string GetLocalizedString ( string value )
{
return SR.GetString ( value ) ;
}
}
}
|
Note: This class too caches the localized string, and
this is done by the base class which is a whole load
more difficult to change on the fly. If you ask me this
is a bug, but a pretty small one at that.
The SR class
You may have noticed in the above classes that I've used a class
called 'SR' - this is a class which wraps access to resources for
the current assembly, and provides static methods for retrieving
resources. This wraps all the complexity (well, simplicity if you
ask me!) of accessing resources bound within your assembly (and also
in satellite assemblies), so all I need to do is request the
appropriate attribute. This class is closely modelled on the way
that .NET does it's resource handling...
|
using System;
using System.Globalization;
using System.Resources;
namespace LocalizedPropertyGrid
{
public sealed class SR
{
#region Instance methods and data
private SR ( )
{
_rm = new System.Resources.ResourceManager ( "LocalizedPropertyGrid.LPG" , this.GetType().Assembly ) ;
}
private ResourceManager Resources
{
get { return _rm ; }
}
private ResourceManager _rm ;
#endregion
#region Static methods and data
private static SR GetLoader ( )
{
if ( null == _loader )
{
lock ( _lock )
{
if ( null == _loader )
_loader = new SR ( ) ;
}
}
return _loader ;
}
public static string GetString ( string name )
{
SR loader = GetLoader ( ) ;
string localized = null ;
if ( null != loader )
localized = loader.Resources.GetString ( name , null ) ;
return localized ;
}
public static string GetString ( CultureInfo culture , string name )
{
SR loader = GetLoader ( ) ;
string localized = null ;
if ( null != loader )
localized = loader.Resources.GetString ( name , culture ) ;
return localized ;
}
private static SR _loader = null ;
private static object _lock = new object ( ) ;
#endregion
}
}
|
This class exposes string resources from the main assembly. The constructor
uses a resource name (here LocalizedPropertyGrid.LPG) which is the name
I've given to the localized resources for my test application. I created a
Windows Forms project called LocalizedPropertyGrid, and then added a .resx file
called LPG, so this is where the name comes from. I then created a German
version (LPG.de.resx) and added some 'German' resources into it - well, it's
sort of German, apologies to all those real German speakers out there!. VS.NET
will build appropriate localized assemblies based on the resource files you add,
so that's pretty much all there is to it. Here's a couple of images which show
how this looks on screen.
|
This first image shows the properties of an object in English - the Category and Description are in English

|
This second image shows the properties of an object in German - well, sort of German

|
Summary
In this article I've described how to localize the property grid
control, with some examples of how best to accomplish this. As
usual, .NET supports this facility, you just need to know how to do
it!.
You can download the code (as a Visual Studio.NET v1.1 solution) here.
LocalizedPropertyGrid.zip