From: michelQA on 30 Jan 2010 22:02 In my application when doing PropertyGrid1.Object=button1 is there any way to display .net control object properties descriptions (like the button1.Text property description) in another language (current culture) or it's just available in english in .net ? Thanks!
From: Jesse Houwing on 31 Jan 2010 05:17 * michelQA wrote, On 31-1-2010 4:02: > In my application when doing PropertyGrid1.Object=button1 is there any > way to display .net control object properties descriptions (like the > button1.Text property description) in another language (current > culture) or it's just available in english in .net ? > > Thanks! > Yes it is possible, but it's quite a bit of work. You'll need to write your own propertydescriptors and then devise a system that first tries to look up the property based on the control name/property name in a resource file, and if it can't find it there, just returns the default. When you are only trying to translate your own controls, it gets easier: http://www.morganskinner.com/Articles/LocalizedPropertyGrid/ and a more extensive walkthrough of working with objects under your control: http://www.codeproject.com/KB/miscctrl/globalizedpropertygrid.aspx -- Jesse Houwing jesse.houwing at sogeti.nl
From: michelQA on 31 Jan 2010 23:11 I already know theses links... but thanks for trying to help! :) I dont want to override the description with my own text. My question was about built-in net object... I was just wondering if a kind a localization is provided for descriptions. I mean getting property description in a different language. Ex: Button.text return the description "The text of the control" look like this text is only available in english... so people who use VS in different language read the property description *only* in english??? If not the resources may be installed by VS setup. I'm my application I display a lot of custom objet (globalized for different language in category and description) in a propertyGrid but I'm also displaying standard .net objet (mostly net controls) and it look weird to have these object only in english. Im now thinking about dropping the multilanguage support since the display cannot be uniform in the current culture selected language :( Thanks again
From: Jesse Houwing on 1 Feb 2010 16:56 * michelQA wrote, On 1-2-2010 5:11: > I already know theses links... but thanks for trying to help! :) I > dont want to override the description with my own text. > > My question was about built-in net object... I was just wondering if > a kind a localization is provided for descriptions. I mean getting > property description in a different language. Ex: Button.text return > the description "The text of the control" > > look like this text is only available in english... > > > so people who use VS in different language read the property > description *only* in english??? If not the resources may be > installed by VS setup. > > I'm my application I display a lot of custom objet (globalized for > different language in category and description) in a propertyGrid but > I'm also displaying standard .net objet (mostly net controls) and it > look weird to have these object only in english. Im now thinking > about dropping the multilanguage support since the display cannot be > uniform in the current culture selected language :( > > Thanks again Well to get your own information, you need to implement your own type descriptor. If all your objects are inherited from the default objects. It's pretty easy to create a simple inherited button. Just make sure you always use your own controls... If you're going into grids and third party controls it gets harder. Then create your own GlobalizedTypeDescriptor like this: public class GlobalizedTypeDescriptor : CustomTypeDescriptor { /// <summary> /// Initializes a new instance of the <see cref="GlobalizedTypeDescriptor"/> class. /// </summary> public GlobalizedTypeDescriptor() { } /// <summary> /// Initializes a new instance of the <see cref="GlobalizedTypeDescriptor"/> class. /// </summary> /// <param name="descriptor">The default (fallback) descriptor.</param> public GlobalizedTypeDescriptor(ICustomTypeDescriptor descriptor) : base(descriptor) { } private PropertyDescriptorCollection globalizedProps; /// <summary> /// Called to get the properties of a type. /// </summary> /// <param name="attributes">An array of attributes to use as a filter. This can be null.</param> /// <returns> /// A <see cref="T:System.ComponentModel.PropertyDescriptorCollection"/> containing the property descriptions for the object represented by this type descriptor. The default is <see cref="F:System.ComponentModel.PropertyDescriptorCollection.Empty"/>. /// </returns> public override PropertyDescriptorCollection GetProperties(Attribute[] attributes) { if (globalizedProps == null) { // Get the collection of properties PropertyDescriptorCollection baseProps = base.GetProperties(attributes); globalizedProps = new PropertyDescriptorCollection(null); // For each property use a property descriptor of our own that is able to be globalized foreach (PropertyDescriptor oProp in baseProps) { globalizedProps.Add(new GlobalizedPropertyDescriptor(oProp)); } } return globalizedProps; } /// <summary> /// Returns a collection of property descriptors for the object represented by this type descriptor. /// </summary> /// <returns> /// A <see cref="T:System.ComponentModel.PropertyDescriptorCollection"/> containing the property descriptions for the object represented by this type descriptor. The default is <see cref="F:System.ComponentModel.PropertyDescriptorCollection.Empty"/>. /// </returns> public override PropertyDescriptorCollection GetProperties() { return GetProperties(null); } Your own GlobalizedPropertyDescriptor: /// <summary> /// GlobalizedPropertyDescriptor enhances the base class by obtaining the display name for a property /// from a resource file. /// </summary> public class GlobalizedPropertyDescriptor : PropertyDescriptor { private PropertyDescriptor basePropertyDescriptor; private string localizedName = ""; private String localizedDescription = ""; private String localizedCategory = ""; /// <summary> /// Creates a globalizing property descriptor. /// </summary> /// <param name="basePropertyDescriptor">The default property descriptor to fall back on.</param> public GlobalizedPropertyDescriptor(PropertyDescriptor basePropertyDescriptor) : base(basePropertyDescriptor) { this.basePropertyDescriptor = basePropertyDescriptor; } /// <summary> /// When overridden in a derived class, returns whether resetting an object changes its value. /// </summary> /// <param name="component">The component to test for reset capability.</param> /// <returns> /// true if resetting the component changes its value; otherwise, false. /// </returns> public override bool CanResetValue(object component) { return basePropertyDescriptor.CanResetValue(component); } /// <summary> /// When overridden in a derived class, gets the type of the component this property is bound to. /// </summary> /// <value></value> /// <returns>A <see cref="T:System.Type"/> that represents the type of component this property is bound to. When the <see cref="M:System.ComponentModel.PropertyDescriptor.GetValue(System.Object)"/> or <see cref="M:System.ComponentModel.PropertyDescriptor.SetValue(System.Object,System.Object)"/> methods are invoked, the object specified might be an instance of this type.</returns> public override Type ComponentType { get { return basePropertyDescriptor.ComponentType; } } /// <summary> /// Gets the name that can be displayed in a window, such as a Properties window. /// </summary> /// <value></value> /// <returns>The name to display for the member.</returns> public override string DisplayName { get { // First lookup the property if GlobalizedPropertyAttribute instances are available. // If yes, then try to get resource table name and display name id from that attribute. string baseName = ""; string name = ""; foreach (Attribute attribute in this.basePropertyDescriptor.Attributes) { GlobalizedAttribute globalizedAttribute = attribute as GlobalizedAttribute; if (globalizedAttribute is GlobalizedAttribute) { name = globalizedAttribute.ResourceName; baseName = globalizedAttribute.ResourceBaseName; } } // If no resource table specified by attribute, then build it itself by using namespace and class name. if (baseName.Length == 0) baseName = basePropertyDescriptor.ComponentType.Namespace + "." + basePropertyDescriptor.ComponentType.Name; // If no display name id is specified by attribute, then construct it by using default display name (usually the property name) if (name.Length == 0) name = this.basePropertyDescriptor.DisplayName; // Now use table name and display name id to access the resources. ResourceManager rm = new ResourceManager(baseName, basePropertyDescriptor.ComponentType.Assembly); // Get the string from the resources. // If this fails, then use default display name (usually the property name) string s = null; try { s = rm.GetString(name); } catch (MissingManifestResourceException) { } this.localizedName = string.IsNullOrEmpty(s) ? this.basePropertyDescriptor.DisplayName : s; return this.localizedName; } } /// <summary> /// Gets the description of the member, as specified in the <see cref="T:System.ComponentModel.DescriptionAttribute"/>. /// </summary> /// <value></value> /// <returns>The description of the member. If there is no <see cref="T:System.ComponentModel.DescriptionAttribute"/>, the property value is set to the default, which is an empty string ("").</returns> public override string Description { get { // First lookup the property if there are GlobalizedPropertyAttribute instances // are available. // If yes, try to get resource table name and display name id from that attribute. string baseName = ""; string name = ""; foreach (Attribute attribute in this.basePropertyDescriptor.Attributes) { GlobalizedAttribute globalizedAttribute = attribute as GlobalizedAttribute; if (globalizedAttribute is GlobalizedAttribute) { name = globalizedAttribute.ResourceName; baseName = globalizedAttribute.ResourceBaseName; } } // If no resource table specified by attribute, then build it itself by using namespace and class name. if (baseName.Length == 0) baseName = basePropertyDescriptor.ComponentType.Namespace + "." + basePropertyDescriptor.ComponentType.Name; // If no display name id is specified by attribute, then construct it by using default display name (usually the property name) if (name.Length == 0) name = this.basePropertyDescriptor.DisplayName + "_Description"; // Now use table name and display name id to access the resources. ResourceManager rm = new ResourceManager(baseName, basePropertyDescriptor.ComponentType.Assembly); // Get the string from the resources. // If this fails, then use default empty string indictating 'no description' string s = null ; try { s = rm.GetString(name); } catch (MissingManifestResourceException) { } this.localizedDescription = string.IsNullOrEmpty(s) ? this.basePropertyDescriptor.Description : s; return this.localizedDescription; } } /// <summary> /// Gets the name of the category to which the member belongs, as specified in the <see cref="T:System.ComponentModel.CategoryAttribute"/>. /// </summary> /// <value></value> /// <returns>The name of the category to which the member belongs. If there is no <see cref="T:System.ComponentModel.CategoryAttribute"/>, the category name is set to the default category, Misc.</returns> /// <PermissionSet> /// <IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=2.0.3600.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" version="1" Flags="UnmanagedCode, ControlEvidence"/> /// </PermissionSet> public override string Category { get { // First lookup the property if there are GlobalizedPropertyAttribute instances // are available. // If yes, try to get resource table name and display name id from that attribute. string baseName = ""; string name = ""; foreach (Attribute attribute in this.basePropertyDescriptor.Attributes) { GlobalizedAttribute globalizedAttribute = attribute as GlobalizedAttribute; if (globalizedAttribute is GlobalizedAttribute) { name = globalizedAttribute.ResourceName; baseName = globalizedAttribute.ResourceBaseName; } } // If no resource table specified by attribute, then build it itself by using namespace and class name. if (baseName.Length == 0) baseName = basePropertyDescriptor.ComponentType.Namespace + "." + basePropertyDescriptor.ComponentType.Name; // If no display name id is specified by attribute, then construct it by using default display name (usually the property name) if (name.Length == 0) name = this.basePropertyDescriptor.DisplayName + "_Category"; // Now use table name and display name id to access the resources. ResourceManager rm = new ResourceManager(baseName, basePropertyDescriptor.ComponentType.Assembly); // Get the string from the resources. // If this fails, then use default empty string indictating 'no description' string s = null; try { s = rm.GetString(name); } catch (MissingManifestResourceException) { } this.localizedCategory = string.IsNullOrEmpty(s) ? this.basePropertyDescriptor.Category : s; return this.localizedCategory; } } /// <summary> /// When overridden in a derived class, gets the current value of the property on a component. /// </summary> /// <param name="component">The component with the property for which to retrieve the value.</param> /// <returns> /// The value of a property for a given component. /// </returns> public override object GetValue(object component) { return this.basePropertyDescriptor.GetValue(component); } /// <summary> /// When overridden in a derived class, gets a value indicating whether this property is read-only. /// </summary> /// <value></value> /// <returns>true if the property is read-only; otherwise, false.</returns> public override bool IsReadOnly { get { return this.basePropertyDescriptor.IsReadOnly; } } /// <summary> /// Gets the name of the member. /// </summary> /// <value></value> /// <returns>The name of the member.</returns> public override string Name { get { return this.basePropertyDescriptor.Name; } } /// <summary> /// When overridden in a derived class, gets the type of the property. /// </summary> /// <value></value> /// <returns>A <see cref="T:System.Type"/> that represents the type of the property.</returns> public override Type PropertyType { get { return this.basePropertyDescriptor.PropertyType; } } /// <summary> /// When overridden in a derived class, resets the value for this property of the component to the default value. /// </summary> /// <param name="component">The component with the property value that is to be reset to the default value.</param> public override void ResetValue(object component) { this.basePropertyDescriptor.ResetValue(component); } /// <summary> /// When overridden in a derived class, determines a value indicating whether the value of this property needs to be persisted. /// </summary> /// <param name="component">The component with the property to be examined for persistence.</param> /// <returns> /// true if the property should be persisted; otherwise, false. /// </returns> public override bool ShouldSerializeValue(object component) { return this.basePropertyDescriptor.ShouldSerializeValue(component); } /// <summary> /// When overridden in a derived class, sets the value of the component to a different value. /// </summary> /// <param name="component">The component with the property value that is to be set.</param> /// <param name="value">The new value.</param> public override void SetValue(object component, object value) { this.basePropertyDescriptor.SetValue(component, value); } } Your own TypeDescriptorProvider /// <summary> /// Returns a non-default typedescriptor which fetches the name, description and categories for a property from a resource file. /// </summary> public class GlobalizedTypeDescriptionProvider : TypeDescriptionProvider { private TypeDescriptionProvider defaultTypeDescriptorProvider = TypeDescriptor.GetProvider(typeof(object)); /// <summary> /// Gets a custom type descriptor for the given type and object. /// </summary> /// <param name="objectType">The type of object for which to retrieve the type descriptor.</param> /// <param name="instance">An instance of the type. Can be null if no instance was passed to the <see cref="T:System.ComponentModel.TypeDescriptor"/>.</param> /// <returns> /// An <see cref="T:System.ComponentModel.ICustomTypeDescriptor"/> that can provide metadata for the type. /// </returns> public override ICustomTypeDescriptor GetTypeDescriptor(Type objectType, object instance) { ICustomTypeDescriptor typeDescriptor = defaultTypeDescriptorProvider.GetTypeDescriptor(objectType, instance); return new GlobalizedTypeDescriptor(typeDescriptor); } } And finally your own attribute to override the Globalization settings: /// <summary> /// Can be used to redirect globalization of property names to a different resource file if required. /// Usage: place the attribute on a property. The name of the resourcefile can be set using the /// ResourceBaseName property. The exact name of the resource can be set using the ResourceName property. /// </summary> [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] [Serializable()] public sealed class GlobalizedAttribute : Attribute { /// <summary> /// Name of the resource file /// </summary> private string _resourceBaseName = ""; /// <summary> /// Name of the resource /// </summary> private string _resourceName = ""; /// <summary> /// Constructs a GlobalizedAttribute /// </summary> public GlobalizedAttribute(){} /// <summary> /// Constructs a GlobalizedAttribute /// </summary> /// <param name="resourceBaseName">Sets the name of the resource file</param> public GlobalizedAttribute(string resourceBaseName) { _resourceBaseName = resourceBaseName; } /// <summary> /// Constructs a GlobalizedAttribute /// </summary> /// <param name="resourceBaseName">Sets the name of the resource file</param> /// <param name="resourceName">Sets the name of the resource</param> public GlobalizedAttribute(string resourceBaseName, string resourceName) : this(resourceBaseName) { _resourceName = resourceName; } /// <summary> /// Name of the resource file /// </summary> public string ResourceBaseName { get { return _resourceBaseName; } private set { _resourceBaseName = value; } } /// <summary> /// Name of the resource /// </summary> public string ResourceName { get { return _resourceName; } private set { _resourceName = value; } } } This is all from an old project. The crux lies in the fact that you need to add the TypeDescriptor attribute to the type in question. I'm unsure if you can override a typedescriptor for each and every class in the system. If you could do that, you'd be in business... This implementation requires you to inherit each and every control used in your system. If that's no issue, then you're all set. Your button would be pretty simple: [TypeDescriptionProvider(typeof(System.ComponentModel.Extensions.Globalization.GlobalizedTypeDescriptionProvider))] public class InheritedButton : Button { } That's it. The provider in question will do all the work. You don't even need the whole GlobalizedProperty thingy, unless you want to be able to override the location of the resources (which is handy for re-using existing descriptions). I'll look into the overriding your TypeDescriptor thingy tomorrow. -- Jesse Houwing jesse.houwing at sogeti.nl
From: michelQA on 2 Feb 2010 05:30
So the answer to my question is that Net ONLY provide english description for classes properties like my Button1.Text . The only way to display a button in a propertyGrid and provide propertyDescription for the text property is to create my own strings resources for all supported languages. Right??? In other words displaying .net control in a propertyGrid should done in english and forget about multilanguage support Great code example...I already integrate different interresting things after looking your code :) Thanks!! |