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 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 on 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 how to assign the property LastName to a category named Required Data:
[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 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:
[AttributeUsage(AttributeTargets.Property, AllowMultiple=false,
Inherited=true)]
class GlobalizedCategoryAttribute : CategoryAttribute
{
string table = "";
...
protected override string GetLocalizedString( string value )
{
string baseStr = base.GetLocalizedString( value );
ResourceManager rm =
new ResourceManager(table, Assembly.GetExecutingAssembly() );
try
{
return rm.GetString(value);
}
catch( Exception )
{
return value;
}
}
}
Use this class to decorate a property:
[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. 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:
[GlobalizedProperty("",Table="GlobalizedPropertyGrid.Person")]
public string 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:
[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):
- 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.
</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:
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.

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
| You must Sign In to use this message board. |
|
|
 |
|
 |
If you are using Generics (like I do), there are some problems in the getter of GlobalizedPropertyDescriptor.DisplayName, because basePropertyDescriptor.ComponentType.Name contains something like `1 at the end of the string.
To fix it, change
if (tableName.Length == 0) { tableName = basePropertyDescriptor.ComponentType.Namespace + "." + }
to
if (tableName.Length == 0) { tableName = basePropertyDescriptor.ComponentType.Namespace + "." + basePropertyDescriptor.ComponentType.Name; int genericsPos = tableName.IndexOf('`'); if (genericsPos > 0) tableName = tableName.Substring(0, genericsPos); }
You have to do the same in the getter of GlobalizedPropertyDescriptor.Description and in the setter of GlobalizedCategoryAttribute.TableName
Greetings Stefan Schwarzbach
modified on Friday, February 13, 2009 9:14 AM
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Move strings from code to resx and translate their automatically. Try RGreatEx[^] free.
--- Best regards, Alexander Nesterenko Safe Develop Team www.safedevelop.com
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
1. Why not overriding "Category" property in "GlobalizedPropertyDescriptor" as you did for "DisplayName" and "Description"? It seems to work fine and maybe it fix the problem of choosing the language only at startup? What do you think?
2. I don't like so much how you construct the ResourceManager: in "GlobalizedPropertyDescriptor" with basePropertyDescriptor.ComponentType.Assembly and in "GlobalizedCategoryAttribute" with Assembly.GetExecutingAssembly(). As I have classes in different assemblies I need to have two resources per class, one for localizing category and one for Name and description.
Best regards ppbb
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Good Article.
I have a small doubt regarding Globalization/Localization. Hope you can help me out. I have a web form application where i need to provide more than 50 different localized resource supports. Apart from this, my application supports rich use of stored data (Sql Server Database) and obviously have few list of Listing controls (Repeater , Grid etc).
My requirement comes like this,
a) I need to provide full localized language support in all data which is displaying in each and every page. Regardless of, the name of controls, content of controls etc. To be concise, which ever text that i am displaying through page, need to be localized based on user selected language.
b) Each and every setup needs to contain all supported languages. So user can switch and view content of site in different language.
I stuck in,
1) I am not sure how to localize stored data in database. 2) How will I give or from where i generate localized information for all supported language.
It would be grate if you can help me out through your valid comments.
Sreejith Nair [ My Articles ]
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I was to hide few poperties for the Textbox control, will setting PropertyGrid.SelectObject = Object. Most of the properties are hidden after setting attribute BROWSABLE=false. But the same in not working for Name, Locked, Mofifiers... has anyone come across this.
Varun Gulati
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
|
 |
|
 |
Hi, First thanks a lot for this article, it has been very helpful.
I am creating a multilingual application, but I'm having a problem with the arabic property grid because, as you know arabic is a right to left language, however even though i've managed to localize the property grid and retrieve the data from my resources file and set the RTL property to Yes and have the text indeed displayed from right to left, it still remains incorrectly displayed in terms of logic. In other words, the property name is on the left and the value is on the right, just like in english , the text is in arabic and from right to left though.
Now i don't know if this is because I am missing something, or if its just that the property grid cannot be made to display the name of the property itself on the right and the actual value on the left... Does anybody have a clue? At the moment mine looks something like this: http://www.comzept.de/templates/images/netrix/proparabic.jpg
So is this the best i can do, or is there some way for the property grid to display right to left correctly?
Thanks a lot in advance
|
| Sign In·View Thread·PermaLink | 2.00/5 |
|
|
|
 |
|
 |
Hello everyone,
First of all, congratulations for your article. It´s really good. I have a question: Is it posible to internationalize the tooltips of the propertygrid? (you know, the messages "Categorized" and "Alphabetic" that appear when the mouse enters the upper propertygrid´s buttons (propertytabs)).
I have noticed that their language depends on the language the VS.NET IDE is instaled.
Also, it would be very interesting if I could internationalize the error form that appears when you insert an incorrect property value, too.
Thank you in advance,
Aitor.
Aitor
|
| Sign In·View Thread·PermaLink | 1.80/5 |
|
|
|
 |
|
 |
Interface - I have implemented your work as an interface so if anyone is interested, contact my email and I will send the files.
Category - one time limitation - It was stated in one of the user messages in the original article that this could be handled by keeping a private member with the last culture used. I solved the limitation issue by holding a private member for each of the DisplayName,Description,Category get methods and comparing it to the current one.
--start of code example: public override string DisplayName { get { if (_displayNameLastCulture==System.Threading.Thread.CurrentThread.CurrentUICulture.Name && this._localizedName!="") return this._localizedName;
_displayNameLastCulture = System.Threading.Thread.CurrentThread.CurrentUICulture.Name;
// //reconstruct the localized value string... //
return this._localizedName; } } --end of code
this code should be repeated for the Description and Category properties, and replacing _displayNameLastCulture with _descriptionLastCulture and _categoryLastCulture accordingly.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I made a next evolution of the globalization thing, not needing inheritance at all, by using a TypeConverter. Also I merged in a ripped piece of code for sorting. Have a look at article Sorted globalized property grid. It also globalizes Category, including runtime switching.
All credits to Gerd and the Paul Tingey for the stuff I ripped from ofcourse (mentioned in the article).
Wout
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I congratulations you for your example, for me is excelent. I want to ask you one question: In your example in the class Person I have put one child class and my surprise is that for the atributes of this class the text and description isn´t translated to the current language. If I put: 'public class ChildPerson : GlobalizatedObject' then the child atributtes don´t appears in the property grid. Can I put child class to show child atributtes using your classes? Thank you
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
 |
In my case, it's a thread which set the selectedObject of the property grid... But the scrollbar often dissapear if I do that, here is a solution :
You have to declare a delegate on the method called by your thread and to use the beginInvoke method instead.
AddItemDelegate del = new AddItemDelegate(YOURMETHOD); PROPERTYGRID.BeginInvoke(del);
I have this problem for a long time so if it can help somebody ... 
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Good start, but how to invoke a file dialogue in property grid, so that after closing the dialog the file path will be displayed as in the property value
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Did I miss something but what happened if you call GlobalizedObject.GetProperties(Attribute[]) more than one time but with different attributes list ? As you init globalizedProps just once, it seems that a wrong PropertyDescriptorCollection is returned the second time ?
Nicolas
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
My last attempt at posting this question failed since I was not logged in yet so please delete it as I cannot.
Ok, here it is again:
I have localized the category & property names as per the article. My next question is how to localize a enum for a property in the propertygrid. For example, for a property, I have an enum type such as:
enum YesNoEnum{ Yes=0, No }
The propertygrid will display "Yes" or "No" as a dropdown list that a user can select (for the default language of EN). My question is, in the enum, how can the dropdown show "Oui" and "Non" for say French or support mult languages for that matter? I do not think I want to have mult enum defs (one per language). Is the solution to use EnumBuilder?
|
| Sign In·View Thread·PermaLink | 4.20/5 |
|
|
|
 |
|
 |
Hi Paul,
What about a custom type converter that you associate with your enum type? Before a property value is displayed in the property grid there is request for a type converter associated with its type. This type converter then is responsible for converting your property value to a string and vice versa. When converting you can use a resource manager. Have a look at the EnumConverter type in the msdn help. Anyway, it is more complex as it should be .
Kind regards Gerd
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
 |
I know how to localize the category name but what if a property value is to be localized? I have an enum for a property like:
enum ResponseEnum{
}
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
I'm having some MAD trouble trying to get LTR text in a combo box with RTL (or visa versa)...
Any thoughts? Give this a whirl:
CultureInfo[] cultures = CultureInfo.GetCultures(CultureTypes.InstalledWin32Cultures); foreach (CultureInfo c in cultures) { this.cmbEncoding.Items.Add(c.NativeName); } this.cmbEncoding.SelectedItem = CultureInfo.CurrentUICulture.NativeName;
You're probably gonna need to install Hebrew or Arabic/Farsi for the ability to see it properly...
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Additional info... ...to the localization of category names and its limitations when switching the language at runtime:
Kay-Uwe Schreiner has provided a good alternative with a big advantage: It works!
Have a look at the appropriate message thread attached to my previous article ( http://www.codeproject.com/cs/miscctrl/globalizedpropertygrid.asp?msg=527998#xx522953xx).
Have fun Gerd
|
| Sign In·View Thread·PermaLink | 5.00/5 |
|
|
|
 |
|
 |
I have a situation where all string on my form is localized. I set the form to localizable=false & I created my own resource file. I found that using the resource file tied to a form is problematic. Some strings get deleted for some reason. So, I created my resource files myself. The strings on the form are localized as expected. But, I also have property grids. I did create resource files for those grids, one per language (xxx.fr.resx, xxx.de.resx) and the default resource file for English (xxx.resx). After the build, it creates a \de and \fr dir. When I run it, only some strings in the propoerty grid are being localized in the correct langauge while other strings in teh same grid are still in English.
I used the code straight from the example. And I stepped into the code especially in the categoryattribute function. I check the thread id for say french and indeed i am set for french but the string comes back in english. I do set the thread id in the main form (Onformload) based on the desired lang ('FR', 'DE' or 'EN');
Has anyone had this same situation?
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
This may happen when the resource manager is not able to find the strings in the resource file of the currently set language. Then a fallback occurs to the default language. A string cannot be found due to following reasons: the resource assembly is missing, the string is missing in the resource file, or the string identification is not spelled correctly ( f.e. 'age' instead of 'Age').
You may check. Otherwise it sounds strange to me . Maybe you can provide a sample to have a deeper look.
Hope this helps.
Gerd
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Thanks for the suggestion on checking the labels(the lookup name) for the strings. I had sent the English strings to Europe to be translated. They came back in a spreadsheet. I then cut and pasted the labels and translated strings into my French resource file.
The labels in the French resource file "appeared" to be visually the same as my default resx file but in reality they had some trailing characters. It seems there were hidden trailing characters when I copied the labels into my xxx.fr.resx file. So, after deleting the trailing characters all was fine & it worked. Since the labels in English did not match the labels in the French resx file, the fallback to the default (English) was ocurring.
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Great!
Had a similar issue with non-visible characters some years ago. It may confuse you first. Have fun Gerd
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Excellent article. Gave it top marks.
I'd welcome your thoughts on the practicality of using GlobalizedObject and an interface rather than an object. Doing so will allow the framework to be retrofitted to existing classes without having to change their existing inheritance.
Regards
Bill Seddon
|
| Sign In·View Thread·PermaLink | |
|
|
|
 |
|
 |
Thanks, Bill.  You are right and I agree with you. I also prefer an interface driven approach because it gives you more flexibility. Especially if you are bound to an existing inheritance tree. In addition, from the design view point, you have a tightly coupled base class in your design that is not related to the domain area, but has a support characterization. So decoupling this issue would be a good idea.
I will give it a thought ....
Kind regards Gerd
|
| Sign In·View Thread·PermaLink | 2.00/5 |
|
|
|
 |
|
|