Enumerate object properties at runtime

The property grid control is great to let user see and enter values of properties and easy to use.

For a programmer, it’s also easy to create a class, populate properties and create a PropertyGrid to edit thoses class properties.
Here’s a example:

public class Person
{
   public string Name { get; set; }
   public int Age { get; set; }
   public bool Male { get; set; }
}

...

//Create the property grid
PropertyGrid pg = new PropertyGrid();
pg.Dock = DockStyle.Fill;
this.Controls.Add(pg);

//Create a person
Person hulk = new Person();
hulk.Name = "Hulk";
hulk.Age = 32;
hulk.Male = true;

//Select the object in the property grid
pg.SelectedObject = hulk;

...

Here is a screenshot of what it give:

Example1

Example1

You can decorate the properties with attributes to to add a category for example. Here is a example that add the category attribute and a read-only attribute for the property Name:

public class Person
{
   [CategoryAttribute("General"), ReadOnlyAttribute(true)]
   public string Name { get; set; }</code>

   [CategoryAttribute("General")]
   public int Age { get; set; }

   [CategoryAttribute("Sex")]
   public bool Male { get; set; }
}

Example2

Dynamically populate properties in a object

That’s pretty simple.
What if you have a class that have dynamics properties. You can’t just enumerate the properties and decorate them!
So how to proceed?

The .NET framework provide a mechanism to enumerate the properties at the runtime and provide attributes informations to use.

The interface ICustomTypeDescriptor provides an interface that supplies dynamic custom type information about an object.

You need to implement a few methods, but the principal is GetProperties that return a PropertyDescriptorCollection.

Let’s build a simple example that will provide a container that returns the properties that it contains.

We will create a class PropetyContainer that will implement the interface ICustomTypeDescriptor. The class will inherit from Dictionnary to hold the key/value pair of informations.

Here is the code:

public class PropertyContainer : Dictionary, ICustomTypeDescriptor
{
   public PropertyContainer()
   {
   }

   #region ICustomTypeDescriptor Members</code>

   public AttributeCollection GetAttributes() { return TypeDescriptor.GetAttributes(this, true); }
   public string GetClassName() { return TypeDescriptor.GetClassName(this, true); }
   public string GetComponentName() { return TypeDescriptor.GetComponentName(this); }
   public TypeConverter GetConverter() { return TypeDescriptor.GetConverter(this, true); }
   public PropertyDescriptor GetDefaultProperty() { return TypeDescriptor.GetDefaultProperty(this, true); }
   public object GetEditor(System.Type editorBaseType) { return TypeDescriptor.GetEditor(this, editorBaseType, true); }
   public EventDescriptor GetDefaultEvent() { return TypeDescriptor.GetDefaultEvent(this, true); }
   public EventDescriptorCollection GetEvents(System.Attribute[] attributes) { return TypeDescriptor.GetEvents(this, attributes, true); }
   public EventDescriptorCollection GetEvents() { return TypeDescriptor.GetEvents(this, true); }

   public object GetPropertyOwner(PropertyDescriptor pd) { return this; }
   public PropertyDescriptorCollection GetProperties(System.Attribute[] attributes) { return GetProperties(); }

   public PropertyDescriptorCollection GetProperties()
   {
      PropertyDescriptorCollection pdc = new PropertyDescriptorCollection(null);
      foreach (KeyValuePair prop in this)
      {
         //Create the property and append it to the collection
      }
      return pdc;
   }

   #endregion
}

As you can see the PropertyContainer inherit from Dictionary<string, object>, so we can set values to a key as in:

   PropertyContainer container = new PropertyContainer();
   container[“Name”] = “This is my name”;

By implementing the ICustomTypeDescriptor, we provide type information about ourself so other components can query and use them at runtime.

Now, we need to finish the implementation of the GetProperties method. For now it does nothing.

As you can imagine, the PropertyDescriptorCollection is a collection of PropertyDescriptor. A Property Descriptor provides an abstraction of a property on a class. The PropertyDescriptor is an abstract class, so we must provide our own implementation.

Let’s do it.

Let’s say that we just want to provide the name, the type and if the property is read-only to expose the principle.

public class Property : PropertyDescriptor
{
   #region Fields
   private Type type;
   private bool readOnly;
   #endregion

   public Property(string name, Type type, bool readOnly)
      : base(name, null)
   {
      this.type = type;
      this.readOnly = readOnly;
   }

   public override bool IsReadOnly { get { return this.readOnly; } }
   public override Type PropertyType { get { return this.type; } }
   public override Type ComponentType { get { throw new NotImplementedException(); } }
   public override string DisplayName { get { return this.Name; } }
   public override bool CanResetValue(object component) { return false; }
   public override void ResetValue(object component) { }
   public override bool ShouldSerializeValue(object component) { return true; }

   public override object GetValue(object component)
   {
      PropertyContainer container = component as PropertyContainer;
      if (container == null)
         throw new Exception("Invalid component type");
      return container[Name];
   }

   public override void SetValue(object component, object value)
   {
      if (this.readOnly)
         throw new InvalidOperationException();

      PropertyContainer container = component as PropertyContainer;
      if (container == null)
         throw new Exception("Invalid component type");

      container[Name] = value;
   }
}

Most of the implementation is straightforward. It jus implements fields and assessors.

If you take a look at the GetValue and SetValue, you notice that the Property use the PropertyContainer as the storer.

To work, we need to be able to create the Property and integrate it into the PropertyContainer. To achieve this, let’s create a field and a accessor in the PropertyContainer class:

   #region Fields
   Dictionary&lt;string, Property&gt; propertiesDefinition = new Dictionary&lt;string, Property&gt;();
   #endregion

   public void AddProperty(Property property)
   {
      this.propertiesDefinition.Add(property.Name, property);
   }

Now we can finish the GetProperties implementation as follow:

public PropertyDescriptorCollection GetProperties()
{
   PropertyDescriptorCollection pdc = new PropertyDescriptorCollection(null);
   foreach (KeyValuePair prop in this)
   {
      Property property;
      if ( this.propertiesDefinition.TryGetValue(prop.Key, out property) )
         pdc.Add(property);
   }
   return pdc;
}

Let’s now create a simple application tests with a PropertyGrid that will use the Property and PropertyContainer.

   ...
   //Create the property grid
   PropertyGrid pg = new PropertyGrid();
   pg.Dock = DockStyle.Fill;
   this.Controls.Add(pg);

   PropertyContainer container = new PropertyContainer();
   container.AddProperty(new Property("Name", Type.GetType("System.String"), false));
   container.AddProperty(new Property("Age", Type.GetType("System.Int32"), false));
   container.AddProperty(new Property("Male", Type.GetType("System.Boolean"), false));

   container["Name"] = "Hulk";
   container["Age"] = 32;
   container["Male"] = true;

   //Select the object in the property grid created
   pg.SelectedObject = container;
   ...

The result:
DynamicallyCreatedProperties

  1. bilalmunir80#gmail.com
    February 12, 2011 at 5:07 am

    Very usefull code… thankyou 🙂

  1. June 19, 2009 at 11:01 pm

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: