OK, here's the full version; obviously you can change other discreet
values via the PropertyDescriptor implementation, or any attribute via
GetAttributes. I have implemented it as a static instance here, but you
could merrily recreate for each instance, or even on each call.
Personally I use an extenstion of this in a dynamic property bag
approach.
Most of the code is boiler-plate; ForwardingPropertyDescriptor just
allows a daisy-chain. I've chucked in a demo to illustrate it working
with a property-grid and data-grid-view.
using System;
using System.ComponentModel;
using System.Collections.Generic;
using System.Windows.Forms;
class Program {
static void Main() {
SomeClass test = new SomeClass();
ShowObject(test, "Before");
SomeClass.CountDisplayName = "Toadstool count";
ShowObject(test, "After");
}
private static void ShowObject(SomeClass item, string title) {
using (Form form = new Form())
using (PropertyGrid propGrid = new PropertyGrid())
using (DataGridView gridView = new DataGridView()) {
form.Height = 500;
form.Text = title;
propGrid.Dock = DockStyle.Fill;
gridView.Dock = DockStyle.Bottom;
form.Controls.Add(propGrid);
form.Controls.Add(gridView);
propGrid.SelectedObject = item;
List<SomeClass> items = new List<SomeClass>();
items.Add(item);
gridView.DataSource = items;
form.ShowDialog();
}
}
}
class SomeClass : ICustomTypeDescriptor {
private int count = 5;
[DisplayName("Count of something"), DefaultValue(5)]
public int Count {
get {return count;}
set {count = value;}
}
private string somethingElse = "Empty";
[DisplayName("Another Property"), DefaultValue("Empty")]
public string SomethingElse {
get { return somethingElse; }
set { somethingElse = value; }
}
AttributeCollection ICustomTypeDescriptor.GetAttributes() {
return TypeDescriptor.GetAttributes(GetType());
}
string ICustomTypeDescriptor.GetClassName() {
return TypeDescriptor.GetClassName(GetType());
}
string ICustomTypeDescriptor.GetComponentName() {
return TypeDescriptor.GetComponentName(GetType());
}
TypeConverter ICustomTypeDescriptor.GetConverter() {
return TypeDescriptor.GetConverter(GetType());
}
EventDescriptor ICustomTypeDescriptor.GetDefaultEvent() {
return TypeDescriptor.GetDefaultEvent(GetType());
}
PropertyDescriptor ICustomTypeDescriptor.GetDefaultProperty() {
return TypeDescriptor.GetDefaultProperty(GetType());
}
object ICustomTypeDescriptor.GetEditor(Type editorBaseType) {
return TypeDescriptor.GetEditor(GetType(), editorBaseType);
}
EventDescriptorCollection
ICustomTypeDescriptor.GetEvents(Attribute[] attributes) {
return TypeDescriptor.GetEvents(GetType(), attributes);
}
EventDescriptorCollection ICustomTypeDescriptor.GetEvents() {
return TypeDescriptor.GetEvents(GetType());
}
PropertyDescriptorCollection
ICustomTypeDescriptor.GetProperties(Attribute[] attributes) {
return HackProperties(TypeDescriptor.GetProperties(GetType(),
attributes));
}
PropertyDescriptorCollection ICustomTypeDescriptor.GetProperties()
{
return HackProperties(TypeDescriptor.GetProperties(GetType()));
}
object ICustomTypeDescriptor.GetPropertyOwner(PropertyDescriptor
pd) {
return this;
}
private PropertyDescriptorCollection
HackProperties(PropertyDescriptorCollection properties) {
List<PropertyDescriptor> propList = new
List<PropertyDescriptor>();
foreach (PropertyDescriptor prop in properties) {
if (prop.Name == "Count")
propList.Add(CountPropertyDescriptor);
else
propList.Add(prop);
}
return new PropertyDescriptorCollection(propList.ToArray(),
true);
}
private static readonly DisplayNamePropertyDescriptor
CountPropertyDescriptor;
public SomeClass() { }
public static string CountDisplayName {
get { return CountPropertyDescriptor.DisplayName; }
set { CountPropertyDescriptor.SetDisplayName(value); }
}
static SomeClass() {
CountPropertyDescriptor = new
DisplayNamePropertyDescriptor(TypeDescriptor.GetProperties(typeof(SomeClass))["Count"]);
}
}
public class DisplayNamePropertyDescriptor :
ForwardingPropertyDescriptor {
private string displayName;
public void SetDisplayName(string displayName) {
this.displayName = displayName;
}
public DisplayNamePropertyDescriptor(PropertyDescriptor root)
: base(root) {
SetDisplayName(root.DisplayName);
}
public override string DisplayName {
get { return displayName; }
}
}
public abstract class ForwardingPropertyDescriptor : PropertyDescriptor
{
private readonly PropertyDescriptor _root;
protected PropertyDescriptor Root { get { return _root; } }
protected ForwardingPropertyDescriptor(PropertyDescriptor root)
: base(root) {
_root = root;
}
public override void AddValueChanged(object component, EventHandler
handler) {
Root.AddValueChanged(component, handler);
}
public override AttributeCollection Attributes {
get {
return Root.Attributes;
}
}
public override bool CanResetValue(object component) {
return Root.CanResetValue(component);
}
public override string Category {
get {
return Root.Category;
}
}
public override Type ComponentType {
get { return Root.ComponentType; }
}
public override TypeConverter Converter {
get {
return Root.Converter;
}
}
public override string Description {
get {
return Root.Description;
}
}
public override bool DesignTimeOnly {
get {
return Root.DesignTimeOnly;
}
}
public override string DisplayName {
get {
return Root.DisplayName;
}
}
public override bool Equals(object obj) {
return Root.Equals(obj);
}
public override PropertyDescriptorCollection
GetChildProperties(object instance, Attribute[] filter) {
return Root.GetChildProperties(instance, filter);
}
public override object GetEditor(Type editorBaseType) {
return Root.GetEditor(editorBaseType);
}
public override int GetHashCode() {
return Root.GetHashCode();
}
public override object GetValue(object component) {
return Root.GetValue(component);
}
public override bool IsBrowsable {
get {
return Root.IsBrowsable;
}
}
public override bool IsLocalizable {
get {
return Root.IsLocalizable;
}
}
public override bool IsReadOnly {
get { return Root.IsReadOnly; }
}
public override string Name {
get {
return Root.Name;
}
}
public override Type PropertyType {
get { return Root.PropertyType; }
}
public override void RemoveValueChanged(object component,
EventHandler handler) {
Root.RemoveValueChanged(component, handler);
}
public override void ResetValue(object component) {
Root.ResetValue(component);
}
public override void SetValue(object component, object value) {
Root.SetValue(component, value);
}
public override bool ShouldSerializeValue(object component) {
return Root.ShouldSerializeValue(component);
}
public override bool SupportsChangeEvents {
get {
return Root.SupportsChangeEvents;
}
}
public override string ToString() {
return Root.ToString();
}
}