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

Dynamically Display the Properties Using C# PropertyGrid

0.00/5 (No votes)
20 Jan 2016 1  
Use PropertyGrid to show the object's runtime properties

Introduction

Normal C# PropertyGrid is good enough to display an object’s properties when you select it into the PropertyGrid object and show it. PropertyGrid is smart enough to know the property data type and display the property value either in text box, or combobox. Here, I want to show you an example of how to use it to show the properties added at runtime.

Using the Code

This is MSDN's suggested way to use PropertyGrid class:

  1. Define your object and use property descriptions:
    class  BOOK
    {
             [BrowsableAttribute(false)]
          public int book_id { get;set}
            [BrowsableAttribute(true)]
            [Description("Book’s title")]
            [DisplayName("Title")]
          public string title {get;set}
            [Description("Book’s author")]
            [DisplayName("Author")]
          public string author {get;set}
     [Description("Book’s price in USD")]
            [DisplayName("Price($)")]
          public float price {get;set}
     public BOOK() //constructor
          {
          }
          /*methods …*/
    }
  2. Select your object into PropertyGrid instance and show it:
    public Form1() {
       // The initial constructor code goes here.
       PropertyGrid propertyGrid1 = new PropertyGrid();
       propertyGrid1.CommandsVisibleIfAvailable = true;
       propertyGrid1.Location = new Point(10, 20);
       propertyGrid1.Size = new System.Drawing.Size(400, 300);
       propertyGrid1.TabIndex = 1;
       propertyGrid1.Text = "Property Grid";
       this.Controls.Add(propertyGrid1);
     BOOK book1 = new BOOK();
       propertyGrid1.SelectedObject = book1;
    }

The project requires to use the C# propertyGrid to display/modify the object’s properties, but the object properties are not predefined in the design time, for example, the object’s properties are in the directory container which is dynamically generated based on different needs, like below BOOK class:

public class UserProperty; //user defined property type
/// <summary>
    /// /
    /// </summary>
    public class LocalProperty
    {
        public string Name{get;set;}
        public string Group{get;set;}  //group name
        public string DisplayName{get;set;} //display name
        public Boolean Display {get;set;} //true to show
        public Boolean Readonly {get;set;} //true is readonly
        public string  Description {get;set;}
        public object  Value {get;set}
        public List<object> Options = new List<object>();
        public LocalProperty(string name, string displayname,
                            string description, object value)
        {
            Display = true;
            Readonly = false;
            //
            Name = name;
                Group = &ldquo;general&rdquo;;
            DisplayName = displayname;
            Description = description;
  Value = value;
        }
        public object ValueEditor()
        {
            //return its editor
                 //null to use system provided editor
            if(_value.GetType().BaseType == typeof(UserProperty)) {
                return  (_value as UserProperty).Editor;
            }
            return null;
        }
         }
    public class LocalProperties : Dictionary<string, LocalProperty>
    {
    }
class  BOOK
{
         LocalProperties properties = new LocalProperties();
public BOOK() //constructor
      {
      }
      /*methods &hellip;*/
}
public Form1() {
   // The initial constructor code goes here.
   PropertyGrid propertyGrid1 = new PropertyGrid();
   propertyGrid1.CommandsVisibleIfAvailable = true;
   propertyGrid1.Location = new Point(10, 20);
   propertyGrid1.Size = new System.Drawing.Size(400, 300);
   propertyGrid1.TabIndex = 1;
   propertyGrid1.Text = "Property Grid";
   this.Controls.Add(propertyGrid1);
 BOOK book1 = new BOOK();
      book1.poperties[&ldquo;title&rdquo;] =
           new LocalProperty(&ldquo;title&rdquo;, &ldquo;Title&rdquo;, 
           &ldquo;Book&rsquo;s title&rdquo;, true, &ldquo;C# for beginner&rdquo;);
      book1.properties[&ldquo;price&rdquo;] =
            new LocalProperty(&ldquo;price&rdquo;,&rdquo;Price($)&rdquo;, 
            &ldquo;Book&rsquo;s price in USD&rdquo;, true,&ldquo;$19&rdquo;);
   propertyGrid1.SelectedObject = book1;
}

To implement this kind of function, we need to implement our owner PropertyGrid Object based on some PropertyGrid interface classes. The most important part is to re-implement the override-able functions provided in ICustomTypeDescriptor, PropertyDescriptor and UITypeEditor. UITypeEditor is used to define the user specific property editor, like a dialog box or a form.

 /// <summary>
        /// ////
        /// </summary>
        private class DynamicPropertyDescriptor : PropertyDescriptor
        {
            private readonly LocalPropertyGridObject _propertyGridObject;
            private readonly LocalProperty _propertyObject;
            private readonly Type _propertyType;
            public DynamicPropertyDescriptor(LocalPropertyGridObject propertyGridObject,
                string propertyName, Type propertyType, LocalProperty propertyObject)
                : base(propertyName, null)
            {
                this._propertyGridObject = propertyGridObject;
                this._propertyObject = propertyObject;
                this._propertyType = propertyType;
                //
                var attributes = new List<Attribute>();
                if (propertyObject.GetOptions().Count > 0
                    && !(_propertyObject.ValueType() == typeof(DateTime)
                          ||_propertyObject.ValueType() == typeof(System.Drawing.Color)))
                {
                    //don't use this for the Datetime and color value
                    // system will create them by default.
                    attributes.Add(new typeConverterAttribute
                    (typeof(OptionTypeConverter))); //this enable to use ComboBox
                }
                attributes.Add(new DisplayNameAttribute(_propertyObject.DisplayName==null?
                                                _propertyObject.Name: _propertyObject.DisplayName));
                attributes.Add(new CategoryAttribute(_propertyObject.Group));
                attributes.Add(new BrowsableAttribute(_propertyObject.Display));
                attributes.Add(new DescriptionAttribute(_propertyObject.Description));
                if (_propertyObject.ValueType().BaseType == typeof(UserProperty))
                {
                    attributes.Add(new EditorAttribute(typeof(UserPropertyEditor), 
                    typeof(UITypeEditor))); //this attribute enable to use user defined editor 
                                            // (dialog box)
                }
                this.AttributeArray = attributes.ToArray();
            }
}
public class LocalPropertyGridObject : DynamicObject, ICustomTypeDescriptor, INotifyPropertyChanged
{
        private LocalPropertyGrid _owner;
        private LocalProperties _properties;
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            var memberName = binder.Name;
            LocalProperty _p;
            if (_properties.TryGetValue(memberName, out _p))
            {
                result = _p.Value;
                return true;
            }
            result = null;
            return false;
        }
        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            var memberName = binder.Name;
            _properties[memberName].Value = value.ToString();
            NotifyToRefreshAllProperties();
            return true;
        }
        public PropertyDescriptorCollection GetProperties()
        {
            // of course, here must be the attributes associated
            // with each of the dynamic properties
             var properties = _properties
                .Select(pair => new DynamicPropertyDescriptor(this,
                    pair.Key, pair.Value.Value.GetType(), pair.Value));
            return new PropertyDescriptorCollection(properties.ToArray());
        }
        public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
            return this.GetProperties();
        }
}
        /// </summary>
        private class UserPropertyEditor : UITypeEditor
        {
            public override UITypeEditorEditStyle GetEditStyle(ITypeDescriptorContext context)
            {
                return UITypeEditorEditStyle.Modal;
            }
public override object EditValue
(ITypeDescriptorContext context, System.IServiceProvider provider, object value)
            {
                IWindowsFormsEditorService svc = provider.GetService
                (typeof(IWindowsFormsEditorService)) as IWindowsFormsEditorService;
                DynamicPropertyDescriptor d = context.PropertyDescriptor as DynamicPropertyDescriptor;
                if (d != null)
                {
                     //create the User Editor Instance and display as a model Dialog
                    LocalPropertyEditor user_editor = System.Activator.CreateInstance(
                                 d.GetPropertyObject().ValueEditor().GetType(),
                                 new object[] { value }
                                ) as LocalPropertyEditor;
                    if (svc != null && user_editor != null)
                    {
                        LocalPropertyGridObject localpropertyobject
                            = context.Instance as LocalPropertyGridObject;
                        if (localpropertyobject != null)
                        {
                            user_editor.StartPosition = FormStartPosition.Manual;
                        using (Form form = user_editor as Form)
                        {
                            if (svc.ShowDialog(form) == DialogResult.OK)
                            {
                                //foo.Bar = form.Value; // update object
                                value = user_editor.Value;
                            }
                        }
                    }
                }
                return value; // can also replace the wrapper object here
            }

User defined property editor should be inherited from the class "LocalPropertyEditor" with a constructor to accept property value and a pre-closing callback to set the DialogResult = OK.

class  YourPropertyEditor: LocalPropertyEditor
{
      YourPropertyEditor(string value);
       /// <summary>
        ///
        /// </summary>
        public void OnOK(EventArgs e)
        {
            this.DialogResult = DialogResult.OK;
            this.Close();
        }
}

Check the example attached to see the full source code.

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