Click here to Skip to main content
6,629,885 members and growing! (23,466 online)
Email Password   helpLost your password?
Desktop Development » Grid & Data Controls » PropertyGrid     Intermediate License: The Code Project Open License (CPOL)

DynamicProperties for PropertyGrid

By Mizan Rahman

Tell PropertyGrid what to display, when to display, and how to display
C# (C# 2.0), Windows, .NET (.NET 2.0, .NET 3.0, .NET 3.5), GDI+, Dev
Version:4 (See All)
Posted:22 Feb 2009
Updated:23 Jun 2009
Views:12,720
Bookmarked:71 times
Announcements
Loading...
 
Search    
Advanced Search
Add to IE Search
printPrint   add Share
      Discuss Discuss   Broken Article?Report  
10 votes for this article.
Popularity: 4.47 Rating: 4.47 out of 5

1

2
1 vote, 10.0%
3
3 votes, 30.0%
4
6 votes, 60.0%
5

Introduction

PropertyGrid control is one of most coveted controls provided by Microsoft. Just by assigning your object to its SelectObject property allows the user to modify the object's public properties. Using it, we can be less dependent on dialogs based user interface (Edit controls, ListBox, ComboBox, etc.) to get user input, rather now we can use this generic user interface to get user input. But wait, in dialog box I could disable a control or hide a control based on user selection of another control or controls. How do I do this with PropertyGrid or can I do this? The answer is yes, you can do it. But it requires filling in lots of bits and pieces at the appropriate places and one has to be quite knowledgeable about how PropertyGrid works. Well I got my knowledge on this by searching the net and by reading MSDN over and over. In this article, I will show you how you can have these following features in your class:

  • Adding a new property to your class on the fly
  • Getting property change notification to your code for the property you create on the fly
  • Hiding a property
  • Disabling a property
  • Hiding a member of an enumeration data type
  • Showing a member of an enumeration data type
  • Adding a display name to a member of enumeration data type
  • Adding description to a member of enumeration data type
  • Providing editor for an enumeration data type that has System.FlagsAttribute
  • New feature (23 June, 2009): SortOrderAttribute allows you specify the sort order of each properties.

All these can be done at runtime. During my search, I found some articles which I have listed at end of this article. They have few of these features in limited form. What I tried to do with my solution was to make a generic solution for the developer so she/he can tell PropertyGrid what to display, when to display, and how to display from within their class.

Design Goals

After looking at the articles, I defined a set of design goals:

  • Solution should include a creation of new PropertyGrid, derived from System.Windows.Forms.PropertyGrid.
  • Solution should not require that user class be derived from a class or interface.
  • Solution should be a set of attributes used by user class.
  • Solution should use a callback function to allow the object to modify its properties by itself.

Using the Code

Here are the classes that make up the solution:

I will now briefly explain how these classes work. You can of course look at the code to know about these classes in detail. Code for these classes is fairly straight forward and with comments:).

DynamicCustomTypeDescriptor

This class maintain a collection of all the properties of your class and caches for performance gain. The collection also contains any property you create on-the-fly. This class is used by DynamicTypeDescriptorProvider class.

DynamicTypeDescriptorProvider

This is the class that makes it all possible. You use this class type in the construction of System.ComponentMode.TypeDescriptionProviderAttribute attribute.You must attach this attribute to your class for your class to be able to show/hide/create properties on-the-fly. And you must have a callback function with the following signature in your class:

public void ModifyDynamicProperties
	(System.ComponentModel.PropertyDescriptorCollection  pdc)

PseudoPropertyDescriptor

This class is used from within your class' callback function, allowing you to create a new property of your object.

EnumEditorUI

This class allow you to display your enumeration data type in a fancy way where you could have a description of each member of the enumeration. For enumeration with System.FlagsAttribute attribute, it will display CheckListBox control instead of a simple ListBox control. This class will make use of EnumTypeConverter and/or EnumItemAttribute classes if they are present with the enumeration type.

EnumTypeEditor

You use this class type in the construction System.ComponentModel.EditorAttribute. You must have this attribute on the enumeration type itself or a property with the enumeration type to make use of the EnumEditorUI.

EnumItemAttribute

This attribute is used on the member of an enumeration. It has three properties: DisplayName, Browsable, and Description. This class is used by both EnumTypeConverter and/or EnumItemAttribute classes if present. The Description is only used by EnumTypeEditor class and in turn by EnumEditorUI class.

EnumTypeConverter

If you want to hide a member of an enumeration, you must attach this converter through System.ComponentModel.TypeConverterAttribute attribute on your enumeration.

Below is a code snippet that shows how to use the solution for a sample class:

  [TypeConverter(typeof(EnumTypeConverter))]
  [Editor(typeof(EnumTypeEditor), typeof(UITypeEditor))]
  [Flags]
  public enum Days
  {
    [EnumItem("Not Selected", true, "Event will not reoccur.")]
    None = 0,

    [EnumItem("Monday", true, "Day of the Moon.")]
    Mon = 1,

    [EnumItem("Tuesday", true, "Day of Mars.")]
    Tue = 2,

    [EnumItem("Wednesday", true, "Day of Mercury.")]
    Wed = 4,

    [EnumItem("Thursday", true, "Day of Jupiter.")]
    Thr = 8,

    [EnumItem("Friday", true, "Venus's day.")]
    Fri = 16,

    // hide this one, just for the sake of it
    [EnumItem("Saturday", false, "Day of Saturn.")]  
    Sat = 32,

    // hide this one, just for the sake of it
    [EnumItem("Sunday", false, "Day of the sun.")]   
    Sun = 64,

    [EnumItem("Weekdays", true, "All days except Saturday and Sunday.")]
    Work = Days.Mon | Days.Tue | Days.Wed | Days.Thr | Days.Fri,

    [EnumItem("Weekend", true, "Only Saturday and Sunday.")]
    NoWork = Days.Sat | Days.Sun,
  }

  public enum Action
  {
    Hide,
    Show,
    MakeReadOnly,
  }

  [TypeDescriptionProvider(typeof(DynamicTypeDescriptionProvider))]
  public class TestClass
  {
    public TestClass()
    {
    }
    private Days m_PropertyA = Days.None;

    [Description("Demonstrate how to work with enumeration data type in PropertyGrid.")]
    public Days PropertyA
    {
      get
      {
        return m_PropertyA;
      }
      set
      {
        m_PropertyA = value;
      }
    }

    private Action m_PropertyB = Action.Show;
    [Description("Changing the value of this property will effect PropertyC")]
    [RefreshProperties(RefreshProperties.All)]  // this attribute is needed
    public Action PropertyB
    {
      get
      {
        return m_PropertyB;
      }
      set
      {
        m_PropertyB = value;
      }
    }

    private string m_PropertyC = String.Empty;
    [Description("This property's behaviour is controlled by PropertyB.")]
    public string PropertyC
    {
      get
      {
        return m_PropertyC;
      }
      set
      {
        m_PropertyC = value;
      }
    }

    private bool m_PropertyD = false;
    [Description("Setting this value to true will show a 
	new property of type boolean with name 'PropertyE'.")]
    [RefreshProperties(RefreshProperties.All)]  // this attribute is needed
    public bool PropertyD
    {
      get
      {
        return m_PropertyD;
      }
      set
      {
        m_PropertyD = value;
      }
    }

    /// <summary>
    /// This is a callback function for DynamicTypeDescriptionProvider.
    /// You can modify the collection in this method.
    /// Things you can do in this method:
    ///   Hide a property
    ///   Show a property
    ///   Add/Remove attributes of a property
    ///   Create a new property on the fly
    /// </summary>
    /// <param name="pdc">
    public void ModifyDynamicProperties(PropertyDescriptorList pdl)
    {
      PropertyDescriptor pd = pdl.Find("PropertyC", false);
      Debug.Assert(pd != null);

      pdl.Remove(pd);
      
      switch (PropertyB)
      {
        case Action.Show:
          pdl.Add( TypeDescriptor.CreateProperty( this.GetType(), 
		pd, new BrowsableAttribute(true), 
                  new ReadOnlyAttribute(false)));             
          break;
        case Action.Hide:
          pdl.Add(TypeDescriptor.CreateProperty(this.GetType(), 
		pd, new BrowsableAttribute(false)));
          break;
        case Action.MakeReadOnly:
          pdl.Add(TypeDescriptor.CreateProperty(this.GetType(), 
		pd, new BrowsableAttribute(true),
                  new ReadOnlyAttribute(true)));
          break;
      }

      PropertyDescriptor pdOnFlyE = pdl.Find("PropertyE", false);
      
      if (PropertyD == true && pdOnFlyE == null)
      {
        PropertyDescriptor pdNew = new PseudoPropertyDescriptor
				     (this, true, "PropertyE", typeof(bool),
                                          new BrowsableAttribute(true),
                                          new ReadOnlyAttribute(false),
                                          new DescriptionAttribute("This property was 
					created on the fly(Pseudo)."), 
                                          new SortOrderAttribute(1));
        pdNew.AddValueChanged(this, new EventHandler(this.OnChanged)); // we want a 
							// change notification

        // NOTE: In you practical scnerio, you would probably 
        // keep a reference of this new PropertyDescriptor
        // one class level memeber variable.  
        // This way you can access its info whenever you need it

        pdl.Add(pdNew);
      }
      else if (PropertyD == false && pdOnFlyE != null)
      {
        pdl.Remove(pdOnFlyE);

      }

      pdl.SortUsingSortAttribute();

    }
    private void OnChanged(object sender, EventArgs e)
    {
      PsudoPropertyDescriptor ppd = (PsudoPropertyDescriptor)sender;

      Console.WriteLine("OnChanged for property '" + ppd.Name + "', 
		new value is: "  + ppd.GetValue(this).ToString() );
    }
  }

Points of Interest

When you get into the System.ComponentModel.TypeDescriptor class, it is interesting to the number of classes that play a role in providing a description of a type.

References

History

  • 20th February, 2009: Initial version
  • 23rd June, 2009: Updated demo and source code, slight modification to article

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Mizan Rahman


Member

Location: Satellite Provider Satellite Provider

Other popular Grid & Data Controls articles:

Article Top
You must Sign In to use this message board.
FAQ FAQ 
 
Noise Tolerance  Layout  Per page   
 Msgs 1 to 25 of 39 (Total in Forum: 39) (Refresh)FirstPrevNext
GeneralRefreshProperties.All Pinmemberchrismisztur5:11 22 Oct '09  
GeneralRe: RefreshProperties.All Pinmemberchrismisztur5:23 22 Oct '09  
GeneralRe: RefreshProperties.All PinmemberMizan Rahman6:02 22 Oct '09  
QuestionHandling Inheritance- StackOverflowException [modified] PinmemberPeter Bingel5:30 7 Oct '09  
GeneralGetProperties called more than once Pinmemberchrismisztur9:31 18 Aug '09  
GeneralRe: GetProperties called more than once PinmemberMizan Rahman2:38 19 Aug '09  
GeneralRe: GetProperties called more than once Pinmemberchrismisztur3:43 19 Aug '09  
GeneralRe: GetProperties called more than once PinmemberMizan Rahman4:53 19 Aug '09  
GeneralRe: GetProperties called more than once Pinmemberchrismisztur10:56 26 Aug '09  
GeneralIs it possible to define a category name for a dynamic property ? Pinmemberabrasat3:39 27 May '09  
GeneralRe: Is it possible to define a category name for a dynamic property ? PinmemberMizan Rahman21:49 2 Jun '09  
GeneralRe: Is it possible to define a category name for a dynamic property ? Pinmemberabrasat22:24 9 Jun '09  
GeneralRe: Is it possible to define a category name for a dynamic property ? PinmemberMizan Rahman23:04 9 Jun '09  
GeneralRe: Is it possible to define a category name for a dynamic property ? Pinmemberabrasat23:21 9 Jun '09  
GeneralRe: Is it possible to define a category name for a dynamic property ? PinmemberMizan Rahman5:56 16 Jun '09  
GeneralRe: Is it possible to define a category name for a dynamic property ? Pinmembergeo_m21:33 23 Jun '09  
GeneralRe: Is it possible to define a category name for a dynamic property ? PinmemberMizan Rahman21:41 23 Jun '09  
GeneralRe: Is it possible to define a category name for a dynamic property ? Pinmembergeo_m21:54 23 Jun '09  
GeneralRe: Is it possible to define a category name for a dynamic property ? PinmemberMizan Rahman23:13 23 Jun '09  
GeneralRe: Is it possible to define a category name for a dynamic property ? Pinmembergeo_m23:50 23 Jun '09  
GeneralRe: Is it possible to define a category name for a dynamic property ? PinmemberMizan Rahman21:39 23 Jun '09  
GeneralRe: Is it possible to define a category name for a dynamic property ? Pinmemberabrasat6:18 29 Jun '09  
GeneralRe: Is it possible to define a category name for a dynamic property ? PinmemberMizan Rahman22:55 29 Jun '09  
GeneralRe: Is it possible to define a category name for a dynamic property ? Pinmemberabrasat0:34 30 Jun '09  
GeneralRe: Is it possible to define a category name for a dynamic property ? PinmemberMizan Rahman1:29 30 Jun '09  

General General    News News    Question Question    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

PermaLink | Privacy | Terms of Use
Last Updated: 23 Jun 2009
Editor: Sean Ewington
Copyright 2009 by Mizan Rahman
Everything else Copyright © CodeProject, 1999-2009
Web17 | Advertise on the Code Project