Check out my new blog at https://shibumiware.blogspot.com

Tuesday, May 27, 2008

Simple Property Editor using Reflection in C#

I am working on an internal project where I need to be able to pass an object that has both read and write properties to a simple editor.  The editor must understand when a property can be changed and how to do conversions correctly back into the original object.  I use reflection to accomplish this.  See the scree below:

image

Notice the Property Description column.  A "friendly" description of each property is achieved through the DescriptionAttribute.  Here is an example of applying this attribute to a property:

[Description("Specifies whether scheduling constraints take precedence over dependencies.")]
public bool HonorConstraints
{
    get { return _HonorConstraints; }
    set { _HonorConstraints = value; }
}
 
Here is the object creation and the instantiation of the property editor:
 
ProjectCreationOptions options = new ProjectCreationOptions 
                                     {   AutoCalculateActualAndRemainingWorkAndCosts = true, 
                                         CalculateActualCosts = false, 
                                         CalculateMultipleCriticalPaths = true, 
                                         CalendarGuid = Guid.NewGuid(), 
                                         /*Guid = Guid.NewGuid(),*/ 
                                         HonorConstraints = true, Name = "test"
                                     };

PropertyEditorForm propertyEditorForm = new PropertyEditorForm(options);

if (propertyEditorForm.ShowDialog() == DialogResult.OK)
{
    Debug.Print((propertyEditorForm.Object as ProjectCreationOptions).Name);
}

The form's constructor looks like this:

public PropertyEditorForm(object objectWithProperties)
  {
      InitializeComponent();

      _ObjectWithProperties = objectWithProperties;

      Type type = _ObjectWithProperties.GetType();

      _PropertyInfo = type.GetProperties();

      foreach (PropertyInfo property in _PropertyInfo)
      {
          object[] attributes = property.GetCustomAttributes(false);

          string description = string.Empty;

          foreach (object attribute in attributes)
          {
              var descriptionAttribute = attribute as DescriptionAttribute;

              if (descriptionAttribute != null)
              {
                  description = descriptionAttribute.Description;
              }
          }

          object returnValue = property.GetValue(_ObjectWithProperties, null);

          if (returnValue == null)
          {
              returnValue = string.Empty;
          }

          propertiesGridView.Rows.Add(new object[] {property.Name, description, returnValue.ToString()});

          if (!property.CanWrite)
          {
              propertiesGridView.Rows[propertiesGridView.Rows.Count - 1].ReadOnly = true;
              propertiesGridView.Rows[propertiesGridView.Rows.Count - 1].DefaultCellStyle.BackColor = Color.SlateGray;
          }
      }
  }

The essence of what is going on above is this: 

  • Get the properties from the incoming type (type.GetProperties())
  • For each property, get the custom attributes (in order to get at the DescriptionAttribute)
  • If the description is defined, store it
  • Get the value of the property for the particular instance
  • If the value of the property is null, convert it to string.empty
  • Add a new row to the property editor data grid view
  • If the property is read-only, set the row to read-only and make its background gray

In the editor, if the row is not read-only, changes can be made to the values in the furthest right column.  Clicking "OK" persists those values back into the object, which in turn is available as a public property of the editor dialog.  The code looks like this (error handling removed):

string propertyName = (string) row.Cells[PROPERTY_NAME_COLUMN_INDEX].Value;

foreach (PropertyInfo propertyInfo in _PropertyInfo)
{
    if (propertyInfo.Name == propertyName && propertyInfo.CanWrite)
    {
        object value = 
            TypeDescriptor.GetConverter(propertyInfo.PropertyType).ConvertFrom(row.Cells[PROPERTY_VALUE_COLUMN_INDEX].Value);                        

        propertyInfo.SetValue(_ObjectWithProperties, value, null);

        break;
    }
}

The interesting part here is the call to TypeDescriptor.GetConverter handles the conversion for simple types.

Hope this helps someone.

3 comments :

Mithun Mithun said...

Thanks for your informative article .its very useful.best dot net training in chennai | dot net training institutes in chennai | dot net training institutes in velachery

preethi s said...

It is really a great work and the way in which u r sharing the knowledge is excellent.
Thanks for helping me to understand this concepts. As a beginner in Dot Net programming your post help me a lot.Thanks for your informative article.
best-dotnet-training-in-chennai |
dot net training in chennai

preethi s said...

It is really a great work and the way in which u r sharing the knowledge is excellent.
Thanks for helping me to understand basic concepts. As a beginner in Dot Net programming your post help me a lot.Thanks for your informative article.
dot net training in velachery

Disclaimer

Content on this site is provided "AS IS" with no warranties and confers no rights. Additionally, all content on this site is my own personal opinion and does not represent my employer's view in any way.