title: PropertyGrid with Custom PropertyDescriptor
body: <p>I've been struggling quite a bit to get custom PropertyDescriptors to work the way I want with a PropertyGrid.</p>
  
  <p>The premise: </p>
  
  <ul>
  <li>I have a class called "Animal" which contains the properties <code>Age</code>, <code>Type</code>, <code>Location</code>, and <code>Name</code>.</li>
  <li>I have another class called "AnimalGroup" which contains a <code>List&lt;Animal&gt;</code> where animals are stored.  The class member that contains this list is called <code>Members</code>.</li>
  <li>In the UI of the program, a <code>PropertyGrid</code> form element will list off the Animals in the AnimalGroup that is assigned to the PropertGrid's <code>SelectedObject</code> property.</li>
  </ul>
  
  <p>The third bullet I can get working quite easily (see image below).</p>
  
  <p><img src="https://i.stack.imgur.com/mBvHx.png" alt="PropertyGrid showing the members of an AnimalGroup."></p>
  
  <p>The AnimalGroup class implements the <code>ICustomTypeDescriptor</code> interface to achieve the listing functionality of the Animals in its <code>Members</code> class member.</p>
  
  <p>Unfortunately, as you can see in the image above, the property value is simply the value returned from the Animal's ToString method.</p>
  
  <p><strong>I'd like to allow users the ability to edit the members of each Animal in the PropertyGrid (Age, Type, Location, and Name).  How can I achieve this?</strong>  I assume there will need to be a custom editor of some type.  Honestly, however, I have no idea where to get started as Googling hasn't turned up much for this.</p>
  
  <p>Here are the tutorials I followed to get to this point:</p>
  
  <ul>
  <li><a href="http://www.codeproject.com/Articles/9280/Add-Remove-Items-to-from-PropertyGrid-at-Runtime" rel="nofollow noreferrer">Add (Remove) Items to (from) PropertyGrid at Runtime</a></li>
  <li><a href="http://www.differentpla.net/content/2005/02/using-propertygrid-with-dictionary" rel="nofollow noreferrer">Using PropertyGrid with a dictionary object</a> (this tutorial seems really old)</li>
  </ul>
  
  <p>And if you need it, here's my code (using .NET 4.0):</p>
  
  <p><strong>Animal.cs</strong></p>
  
  <pre><code>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;
      }
  }
  </code></pre>
  
  <p><strong>AnimalGroup.cs</strong></p>
  
  <pre><code>public class AnimalGroup : ICustomTypeDescriptor
  {
      public List&lt;Animal&gt; Members;
  
      public AnimalGroup()
      {
          this.Members = new List&lt;Animal&gt;();
      }
  
      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;
      }
  }
  </code></pre>
  
  <p><strong>AnimalPropertyDescriptor.cs</strong></p>
  
  <pre><code>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)
      {
          // Not relevant.
      }
  
      public override void SetValue(object component, object value)
      {
          this.property = (Animal)value;
      }
  
      public override bool ShouldSerializeValue(object component)
      {
          return false;
      }
  }
  </code></pre>
  
  <p>FYI, I am aware that the code is a bit ridiculous (Animals with names, animal groups, etc).</p>