Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Automatic Expandable Properties in a PropertyGrid

0.00/5 (No votes)
4 Jan 2006 1  
The article describes a family of classes that will automatically enable each of your custom-type's public properties to be expandable in a PropertyGrid without the need to write an explicit TypeConverter. You will also be able to edit those properties that support the 'set' accessor.

Introduction

This article explains adding PropertyGrid support to your custom-types without an associated TypeConverter.

When I finally bit the bullet and moved from Visual Studio 2002 up to Visual Studio 2005, I was disappointed (to put it mildly) to discover that when I selected an instance of my Form-class into a PropertyGrid, none of my custom-type public properties were either expandable or editable as (I am sure!) they used to be. All I could see for each of my properties was a single, relatively useless GridItem in grey-text showing <namespace>.<type-name>.

Of course, by overriding ToString() in each of my custom-types, I could have made the display a little more aesthetically pleasing, I could even display the type�s fields and properties using this approach. However, this wouldn�t have allowed any of those objects to be edited, or even indicate which one�s were editable and which were read-only. Surely, the most useful feature of PropertyGrid a'la 2002 was the way it allowed you to very rapidly provide a type-editor/inspector for your custom-types.

Initially, I spent a day, or so, decorating my classes and their members with various combinations of ComponentModel attribute-tags, in an attempt to get the behaviour that:

  1. I wanted, and
  2. the attribute-names suggested they would provide.

Hah! There followed then a very painful 3-4 days scouring MSDN, forums, etc., initially trying to discover Microsoft�s explanation for the change in behaviour and an associated work-around (fat chance!); then, when this search proved fruitless, to find someone else who had hit and, more importantly solved the same problem. I didn�t have much luck. I did find a few posts on various sites from one Dev. Manager in the US who had been through the same thing back in June this year, but when I mailed him to ask about a solution, he wrote back and said he�d given-up and gone another route.

Finally, I began to realize, although I admit, it took some time for me to accept it, that the only solution (according to Microsoft documentation at least) was to write a custom TypeConverter for every one of my custom-property types! This would enable PropertyGrid to see each custom-type as the aggregation of subtypes that I wanted. This seemed like a pretty big ask!

Then I found Stephen Toub�s excellent NetMatters articles: see URLs below in Background.

  • NET Matters ICustomTypeDescriptor, Part 1 -- MSDN Magazine, April 2005
  • NET Matters ICustomTypeDescriptor, Part 2 -- MSDN Magazine, May 2005

�in which he introduces a family of classes: FieldsToPropertiesTypeDescriptor, FieldsToPropertiesTypeDescriptorProvider, etc., whose purpose is, given an existing type MyClass (say) that exposes only public-access fields (i.e. no Property support), to automatically wrap each field in a pseudo-property that will display in a PropertyGrid without your having to modify the original MyClass at all.

I would encourage anyone interested in PropertyGrid to read these two articles to gain a better understanding of its internal operation; also, to help understand the code presented here.

Although FieldsToPropertiesTypeDescriptor, et. al. didn�t quite provide the desired behaviour, it was possible to use the same technique and architecture to create a solution to my own particular problem. The results are presented below as ExpandableObject and ExpandablePropertiesTypeDescriptionProvider.

Note that the cache-mechanism used in ExpandableObject, in addition to the basic architecture, is lifted from the code in Stephen Taub�s article. Thanks to the author for the information/techniques covered therein and permission to reuse for non-profit making purposes.

Background

Recommended reading:

Using the code

To add support for PropertyGrid expandable/editable public properties to your class, simply derive it from ExpandableObject, e.g.:

using ExpandablePropertiesTypeDescriptor;

public class MyCustomType : ExpandableObject
{
    private int myIntField = 0;
    public int MyIntProperty
    {
        get{ . . . }
        set{ . . . }
    }
    ...
}

Now, when you select an instance of MyCustomType into a PropertyGrid, e.g.:

propertyGrid1.SelectedObject = MyCustomTypeInstance;

you will see all its public-access properties displayed under the usual [+] widget. Any property that supports �set� will also be editable in the PropertyGrid.

Note that, if it is not convenient for you to inherit from ExpandableObject, e.g., the object that you want to select in PropertyGrid is a Form (say) and you want the PropertyGrid to display its (the Form�s) public properties, you can do the following:

using ExpandablePropertiesTypeDescriptor;

public partial class MyFormClass : Form
{
    public MyFormClass()
    {
       TypeDescriptor.AddProvider(new 
           ExpandablePropertiesTypeDescriptionProvider(GetType()), 
           this);
       InitializeComponent();
    }
}

Important!

If you use the scenario shown above, be sure to call TypeDescriptor.RemoveProvider before your application closes. You can do this in the Form's Dispose() method as follows:

protected override void Dispose(bool disposing)
{       
    if (disposing && (components != null))
    {                  
        components.Dispose();
    }
    TypeDescriptor.RemoveProvider(new 
      ExpandablePropertiesTypeDescriptionProvider(GetType()), 
      this);
    base.Dispose(disposing);
}

Points of Interest

Check-out the ExpandableObjectDemo.DemoForm to see the ExpandableObject used on a custom-type property owned by a form. Notice that both techniques [described above] are used, i.e., ExpandableObjectDemo.CustomType inherits from ExpandableObject whilst ExpandableObjectDemo.DemoForm calls TypeDescriptor.AddProvider / TypeDescriptor.RemoveProvider. This is because it is the Form object that is selected in PropertyGrid. If we only wanted to display ExpandableObjectDemo.CustomType in the PropertyGrid without showing its relationship to the parent object, we need not call the Add/RemoveProvider in the Form object.

I guess the most important lesson I have (re)learned during this episode was: �Don�t mess with your dev. environment unless you (i) enjoy pain and (ii) have nothing better [or more urgent] to do.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here