From: michelQA on
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
* 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
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
* 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
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!!