Click here to Skip to main content
15,860,943 members
Articles / Programming Languages / C#

Globalized Property Grid - Revisited

Rate me:
Please Sign up or sign in to vote.
4.73/5 (36 votes)
14 Jun 20036 min read 173.1K   2.4K   75   27
Localization of category names in a globalized property grid control and considers inheritance issues
This article is an incremental version of its base article Globalized property grid. It discusses the localization of category names and localized property names in case of inheritance.

Sample Image

Sample Image

Introduction

In my article, 'Globalized property grid', I demonstrated how to localize the property names of an object. This article is based on this article incrementally.

Corresponding to questions, I would like to address the following topics here:

  • Localizing category names
  • Inheritance of globalized objects

Localizing category names were left out in my previous article although it is as much of interest. However, localization support of category names is limited as you will see below.

Another question I want to answer is regarding inheritance of objects. Although I answered the question in my previous article, I still want to address it here.

Let's start with category names first.

Category Names

A property grid is able to organize the properties of an object in categories. You can assign a category to the properties of your objects by using an attribute of type CategoryAttribute.

Here is an example of how to assign the property LastName to a category named Required Data:

C#
[Category("Required Data")]
public string LastName { get {...}; set {...}; } 

If no category attribute is assigned to a property, then a property will be assigned to a default category that you can find in a static property called CategoryAttribute.Default ("Misc").

CategoryAttribute has a property called Category that is used to access the category name.

If you use category names in a globalized property grid, then it is wishful to have the category names localized, too.

Localizing Category Names

There is support included to localize a category name.

You cannot override the Category property to return a localized string. Instead, the first time the Category property is accessed CategoryAttribute will call an overridable method called GetLocalizedString. This method has a protected scope and is overridable. Derive your own class from CategoryAttribute and override GetLocalizedString. You get the assigned category name (see "Required Data" in the example above) as a parameter and may return a localized string instead:

C#
[AttributeUsage(AttributeTargets.Property, AllowMultiple=false, 
   Inherited=true)]
class GlobalizedCategoryAttribute : CategoryAttribute
{
  string table = "";
  ...
  protected override string GetLocalizedString( string value )
  {
    string baseStr = base.GetLocalizedString( value );
    // Now use resource table name to access the resources.  
    ResourceManager rm = 
       new ResourceManager(table, Assembly.GetExecutingAssembly() );
    // Get the string from the resources.
    try
    {
      return rm.GetString(value);
    }
    catch( Exception )
    {
      return value;
    }
  }
}

Use this class to decorate a property:

C#
[GlobalizedCategory("Required")]
public string LastName { get {...}; set {...}; } 

GlobalizedCategoryAttribute uses the original category names as a symbolic name for accessing the resources. The member table optionally specifies the resource table and may be set either explicitly. If no resource table name is provided, the class name will be used as the table name (see sample).

But there is a big drawback in the implementation of CategoryAttribute provided by the .NET framework (version 1.0).

GetLocalizedString is only called once on the first access. The consequence is we are able to select a language on startup only. Then it will work fine. And maybe this will be sufficient for a lot of applications. In case of switching the language at runtime, it will not work. I demonstrate this issue in the sample provided. On startup, you can select a language. you'll see the property and category names are displayed in the selected language. Pressing the button on the dialog to select another language works for property names only, category names don't change.

I don't know if the behaviour of the CategoryAttribute class has changed in .NET Framework 1.1. Maybe the community can answer.

Let's get over to the second topic to address.

Inheritance of Globalized Objects

In the sample of my previous article (Globalized property grid), I just used one class called Person inheriting the GlobalizedObject. Using the defaults, the localized property names were extracted from the resource file Person.resx or Person.en.resx. If you have tried the sample code and enhanced it by deriving from Person class, then the property names of the derived classes may not be shown. Even though you have placed localized strings in the resource file person.resx.

The answer is relatively simple. By default, the class name will be used to access a localized resource. That means, The person class gets its localized names from person.resx. So, a derived class Employee gets its localized names from Employee.resx.

If you want to use one resource file only, you have to tell it explicitly. Here, two options are possible.

Using the GlobalizedPropertyAttribute class where you can explicitly name the resource table to use on every property individually. Referring to the person.resx resource table may be done as follows:

C#
// Use the provided resource table instead of the default one which 
// would be determined by the class name. The first parameter is 
// the key of the property in the string table. Leaving it empty 
// indicates to use the property name by default.

[GlobalizedProperty("",Table="GlobalizedPropertyGrid.Person")]
public string Department
{
  get { return department; }
  set { department = value; }
}      

Alternatively, an attribute class may be additionally used to define the resource table name on the class level. This is demonstrated by a class GlobalizedObjectAttribute, which contains one property only that specifies a resource table to access. For example, to access a resource table named SpecialStringTable, use it as follows:

C#
[GlobalizedObjectAttribute("GlobalizedPropertyGrid.SpecialStringTable")]
public class Employee : Person
{ 
   ...

The class GlobalizedPropertyDescriptor detects the resource table name by using the following sequence (see implementation of the properties DisplayName and Description):

  1. Check, if there's a GlobalizedPropertyAttribute on property level.
    If yes, additionally check if there is a resource table specified.
  2. If no resource table name provided yet, check, if a GlobalizedObjectAttribute is provided on the class level.
  3. If still no resource table name provided yet, use the class name.

Creating a Sample Project

For demonstration purposes, I have extended the sample project. For a detailed description of the sample project, refer to the previous article (Globalized property grid). There are only some modifications and additions:

Demonstrating Localization of Categories

I have added an attribute class called GlobalizedCategoryAttribute (Attributes.cs) to localize category names. This attribute is applied to the properties as mentioned above. To demonstrate that selecting a language is possible on startup only, I have modified the application a bit. On startup, a dialog box will be displayed to select a language.

Selecting a language

Selecting a language (e.g., English) at this time works for localized category names. The category and property names will be displayed in the selected language in the property grid on the main form.

Image 4

Switching the current language (e.g., to German) by pressing the button in the upper right corner on the main form doesn't affect the category names. Property names have changed correctly.

Image 5

Maybe this limited behaviour of the CategoryAttribute class will change in future releases.

Demonstrating Localization of Properties in Case of Inheritance

I have added an attribute class called GlobalizedObjectAttribute (Attributes.cs) to specify a custom resource table name on class level.

I also have added a class Employee (Employee.cs) inheriting the Person class to demonstrate inheritance.

C#
// Employee.cs
/// <SUMMARY>
/// Employee derives from Person and so is a globalized object implicitly.
/// 
</SUMMARY>
public class Employee : Person
{
  private string department = "";

  public Employee()
  {
  }

  public string Department
  {
    get { return department; }
    set { department = value; }
  }
}        

This class will be instantiated by the Form1 class:

C#
// Instantiate test class and set some data
Employee emp = new Employee();
emp.FirstName = "Max";
emp.LastName = "Headroom";
emp.Age = 42;
emp.Department = "Channel5";

this.person = emp;        

Employee.resx and Employee.en.resx have been to have the property names of Employee displayed correctly.

Employee resource table

Summary

This article is an incremental version of its base article Globalized property grid. It discusses the localization of category names and localized property names in case of inheritance.

To localize the category names, there are two steps necessary:

  • Create a class and derive it from CategoryAttribute and override the method GetLocalizedString. This method should retrieve the localized string from a resource table.
  • Add this attribute to your properties. Provide a name for the category that will be used as a key to the resource string table.

But note: CategoryAttribute only reads the localized string on the first access of the category name. Switching languages at runtime is not possible.

Supporting inherited classes based on the sample provided requires to either define a resource string table for each class with the name of the class or referring to a certain resource table by explicit usage of custom attributes: GlobalizedPropertyAttribute on property level or GlobalizedObjectAttribute on class level.

References

History

  • 15th June, 2003: Initial version

License

This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.

A list of licenses authors might use can be found here.


Written By
Web Developer
Germany Germany
For ten years I worked as a senior consultant, coach, lead architect and project lead for several consulting companies.
Currently I work as a system architect for Zuehlke Engineering GmbH based in Frankfurt.
You can find a detailed resume here.

Comments and Discussions

 
GeneralInterfaces and category limitations Pin
picho8-Jun-04 3:34
picho8-Jun-04 3:34 
GeneralRe: Interfaces and category limitations Pin
wout de zeeuw19-Aug-04 1:50
wout de zeeuw19-Aug-04 1:50 
GeneralGlobalized and nested Pin
pestes12-May-04 20:33
pestes12-May-04 20:33 
GeneralTips if scrollbar dissapears Pin
Grut12-May-04 0:53
Grut12-May-04 0:53 
GeneralProperty Grid and File Open Dialog Pin
w36a4-Feb-04 12:52
w36a4-Feb-04 12:52 
GeneralVery Great but.. Pin
Nycos13-Jan-04 21:42
Nycos13-Jan-04 21:42 
GeneralLocalizing the property value in an enum Pin
pshinohara19-Nov-03 11:45
pshinohara19-Nov-03 11:45 
GeneralRe: Localizing the property value in an enum Pin
Gerd Klevesaat19-Nov-03 22:06
Gerd Klevesaat19-Nov-03 22:06 
GeneralLocalizing the property value in an enum Pin
Anonymous19-Nov-03 11:29
Anonymous19-Nov-03 11:29 
GeneralLocalization of category names Pin
Gerd Klevesaat18-Jun-03 4:31
Gerd Klevesaat18-Jun-03 4:31 
GeneralNot All Strings show up localized Pin
PaulS16-Jun-03 17:07
PaulS16-Jun-03 17:07 
GeneralRe: Not All Strings show up localized Pin
Gerd Klevesaat16-Jun-03 23:24
Gerd Klevesaat16-Jun-03 23:24 
GeneralRe: Not All Strings show up localized Pin
PaulS17-Jun-03 3:04
PaulS17-Jun-03 3:04 
GeneralRe: Not All Strings show up localized Pin
Gerd Klevesaat17-Jun-03 3:30
Gerd Klevesaat17-Jun-03 3:30 
GeneralUsing an interface rather than inheritance Pin
Bill Seddon15-Jun-03 11:31
Bill Seddon15-Jun-03 11:31 
GeneralRe: Using an interface rather than inheritance Pin
Gerd Klevesaat16-Jun-03 22:51
Gerd Klevesaat16-Jun-03 22:51 
GeneralRe: Using an interface rather than inheritance Pin
Michael Coyle20-Mar-05 8:21
Michael Coyle20-Mar-05 8:21 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.