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 lolcalized 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:

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. The 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:

// 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")]
publicstring Department
{
get { return department; }
set { department = value; }
}

Alternatively, an attribute class may be additional 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:

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

Check, if there's a GlobalizedPropertyAttribute on property level.If yes, additionally check if there is a resource table specified.

If no resource table name provided yet, check, if a GlobalizedObjectAttribute is provided on the class level.

If still no resource table name provided yet, use the class name.

Creating a sample project

For demonstration purpose 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 (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.

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.

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.

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

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

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.

Share

About the Author

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.