I try a lot to get custom PropertyDescriptors to work the way I want with a PropertyGrid.
Premise:
- I have a class called "Animal" that contains the properties
Age , Type , Location and Name . - I have another class called "AnimalGroup" that contains a
List<Animal> where animals are stored. The class member containing this list is called Members . - In the user interface of the program, an element of the
PropertyGrid form will display the Animals list in the AnimalGroup, which is assigned to the PropertGrid SelectedObject property.
The third bullet I can easily work (see image below).

The AnimalGroup class implements the ICustomTypeDescriptor interface to achieve the functionality of the Animals enumeration in a member of the Members class.
Unfortunately, as you can see in the image above, the property value is just the value returned from the Animal ToString method.
I want to allow users to edit the members of each Animal in the PropertyGrid (Age, Type, Location and Name). How can i achieve this? I assume that this will require a special editor of some kind. Honestly, however, I have no idea where to start, since Googling is not too much for this.
The following are the tutorials that I read to get to this point:
And if you need it, here is my code (using .NET 4.0):
Animal.cs
public class Animal { private String _Name; public String Name { get { return _Name; } set { _Name = value; } } private String _Type; public String Type { get { return _Type; } set { _Type = value; } } private String _Age; public String Age { get { return _Age; } set { _Age = value; } } private String _Location; public String Location { get { return _Location; } set { _Location = value; } } public override string ToString() { return this.Name + " - " + this.Type; } }
AnimalGroup.cs
public class AnimalGroup : ICustomTypeDescriptor { public List<Animal> Members; public AnimalGroup() { this.Members = new List<Animal>(); } public AttributeCollection GetAttributes() { return TypeDescriptor.GetAttributes(this, true); } public string GetClassName() { return TypeDescriptor.GetClassName(this, true); } public string GetComponentName() { return TypeDescriptor.GetComponentName(this, true); } public TypeConverter GetConverter() { return TypeDescriptor.GetConverter(this, true); } public EventDescriptor GetDefaultEvent() { return TypeDescriptor.GetDefaultEvent(this, true); } public PropertyDescriptor GetDefaultProperty() { return null; } public object GetEditor(Type editorBaseType) { return TypeDescriptor.GetEditor(this, editorBaseType, true); } public EventDescriptorCollection GetEvents(Attribute[] attributes) { return TypeDescriptor.GetEvents(this, attributes, true); } public EventDescriptorCollection GetEvents() { return TypeDescriptor.GetEvents(this, true); } public PropertyDescriptorCollection GetProperties(Attribute[] attributes) { PropertyDescriptor[] pds = new PropertyDescriptor[this.Members.Count]; int i = 0; foreach (Animal a in this.Members) { pds[i] = new AnimalPropertyDescriptor(a, attributes); i++; } return new PropertyDescriptorCollection(pds); } public PropertyDescriptorCollection GetProperties() { return this.GetProperties(new Attribute[0]); } public object GetPropertyOwner(PropertyDescriptor pd) { return this.Members; } }
AnimalPropertyDescriptor.cs
public class AnimalPropertyDescriptor : PropertyDescriptor { private Animal property; public AnimalPropertyDescriptor(Animal target, Attribute[] attrs) : base(target.Name, attrs) { this.property = target; } public override bool CanResetValue(object component) { return false; } public override Type ComponentType { get { return null; } } public override object GetValue(object component) { return property; } public override bool IsReadOnly { get { return false; } } public override Type PropertyType { get { return this.property.GetType(); } } public override void ResetValue(object component) {
FYI, I know that the code is a little ridiculous (animals with names, groups of animals, etc.).