Search code examples
c#.netwinformsstaticpropertygrid

Is there a way to display static properties in a PropertyGrid?


I have a PropertyGrid that displays all of the instance properties for an object. Is there a way to also display the static properties of the class to which the object belongs, either in the same or a separate PropertyGrid? Alternatively, is there another Forms control that would allow me to do this?


Solution

  • Type descriptors are responsible to provide list of properties to show in PropertyGrid.

    To customize list of properties you need to provide custom type description for your class/object using either of the following options:

    Example

    In this example I've implemented the last option. I assume you are going to keep the main class unchanged and just for purpose of showing in PropertyGrid, I've created a custom wrapper object which provides a list of properties for property grid, including the static properties.

    Let's say you have a class like this:

    public class MyClass
    {
        public string InstanceProperty { get; set; }
        public static string StaticProperty { get; set; } = "Test";
    }
    

    And you want to show it's properties in PropertyGrid.

    Then usually the first thing which you need is a new property descriptor:

    using System;
    using System.ComponentModel;
    using System.Linq;
    using System.Reflection;
    public class StaticPropertyDescriptor : PropertyDescriptor
    {
        PropertyInfo p;
        Type owenrType;
        public StaticPropertyDescriptor(PropertyInfo pi, Type owenrType)
            : base(pi.Name,
                  pi.GetCustomAttributes().Cast<Attribute>().ToArray())
        {
            p = pi;
            this.owenrType = owenrType;
        }
        public override bool CanResetValue(object c) => false;
        public override object GetValue(object c) => p.GetValue(null);
        public override void ResetValue(object c) { }
        public override void SetValue(object c, object v) => p.SetValue(null, v);
        public override bool ShouldSerializeValue(object c) => false;
        public override Type ComponentType { get { return owenrType; } }
        public override bool IsReadOnly { get { return !p.CanWrite; } }
        public override Type PropertyType { get { return p.PropertyType; } }
    }
    

    Then you can use either of the options which I mentioned above. For example, here I've created a wrapper type descriptor to not touch the original class:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq;
    using System.Reflection;
    public class CustomObjectWrapper : CustomTypeDescriptor
    {
        public object WrappedObject { get; private set; }
        private IEnumerable<PropertyDescriptor> staticProperties;
        public CustomObjectWrapper(object o)
            : base(TypeDescriptor.GetProvider(o).GetTypeDescriptor(o))
        {
            WrappedObject = o;
        }
        public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
            var instanceProperties = base.GetProperties(attributes)
                .Cast<PropertyDescriptor>();
            staticProperties = WrappedObject.GetType()
                .GetProperties(BindingFlags.Static | BindingFlags.Public)
                .Select(p => new StaticPropertyDescriptor(p, WrappedObject.GetType()));
            return new PropertyDescriptorCollection(
                instanceProperties.Union(staticProperties).ToArray());
        }
    }
    

    And the usage is quite easy:

    var myClass = new MyClass();
    propertyGrid1.SelectedObject = new CustomObjectWrapper (myClass);
    

    enter image description here