There are a number of different ways to do this. The simplest is to do something akin to how certain i18n products do it - subclass the attribute and intercept the text; but this only works if you own the type, and from an attribute you can't access the context.
The next things to look at would be TypeConverter
, since this provides access to the component-model view on the properties, and is simpler than the next two options ;-p This will work with PropertyGrid
, but not DataGridView
etc.
Next on the list is ICustomTypeDescriptor
- not a fun interface to implement, but you can swap in your own property-descriptor. This requires that you own the type (in order to provide the interface support).
Finally, CustomTypeDescriptor
is like the last, but works even for types you don't own, and allows access to tweaked metadata at both the type and object level (everything else only supports object).
Which to choose? I suspect that TypeConverter
would be the most sensible; you need the object-context (which a subclassed attribute doesn't provide), but you don't need the extra complexity.
Here's an example; note that in the TypeConverter
code we have access to the object-context. If the name is simple, then putting it in the TypeConverter
(when creating the property) should suffice.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;
class MyFunkyTypeConverter : ExpandableObjectConverter
{
public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
{
PropertyDescriptorCollection props = base.GetProperties(context, value, attributes);
List<PropertyDescriptor> list = new List<PropertyDescriptor>(props.Count);
foreach (PropertyDescriptor prop in props)
{
switch (prop.Name)
{
case "Distance":
list.Add(new DisplayNamePropertyDescriptor(
prop, "your magic code here"));
break;
default:
list.Add(prop);
break;
}
}
return new PropertyDescriptorCollection(list.ToArray(), true);
}
}
class DisplayNamePropertyDescriptor : PropertyDescriptor
{
private readonly string displayName;
private readonly PropertyDescriptor parent;
public DisplayNamePropertyDescriptor(
PropertyDescriptor parent, string displayName) : base(parent)
{
this.displayName = displayName;
this.parent = parent;
}
public override string DisplayName
{get { return displayName; } }
public override bool ShouldSerializeValue(object component)
{ return parent.ShouldSerializeValue(component); }
public override void SetValue(object component, object value) {
parent.SetValue(component, value);
}
public override object GetValue(object component)
{
return parent.GetValue(component);
}
public override void ResetValue(object component)
{
parent.ResetValue(component);
}
public override bool CanResetValue(object component)
{
return parent.CanResetValue(component);
}
public override bool IsReadOnly
{
get { return parent.IsReadOnly; }
}
public override void AddValueChanged(object component, EventHandler handler)
{
parent.AddValueChanged(component, handler);
}
public override void RemoveValueChanged(object component, EventHandler handler)
{
parent.RemoveValueChanged(component, handler);
}
public override bool SupportsChangeEvents
{
get { return parent.SupportsChangeEvents; }
}
public override Type PropertyType
{
get { return parent.PropertyType; }
}
public override TypeConverter Converter
{
get { return parent.Converter; }
}
public override Type ComponentType
{
get { return parent.ComponentType; }
}
public override string Description
{
get { return parent.Description; }
}
public override PropertyDescriptorCollection GetChildProperties(object instance, Attribute[] filter)
{
return parent.GetChildProperties(instance, filter);
}
public override string Name
{
get { return parent.Name; }
}
}
[TypeConverter(typeof(MyFunkyTypeConverter))]
class MyFunkyType
{
public double Distance {get;set;}
public double AnotherProperty { get; set; }
}
static class Program
{
[STAThread]
static void Main()
{
Application.EnableVisualStyles();
Application.Run(new Form { Controls = {
new PropertyGrid { Dock = DockStyle.Fill,
SelectedObject = new MyFunkyType {
Distance = 123.45
}}
}});
}
}