Search code examples
c#.netwinformspropertygridtypedescriptor

How to add custom Visible property that doesn't hide the control to PropertyGrid?


I'm writing a program that user can edit the control properties in the Form. To change control(textbox, label etc.) properties I am using PropertyGrid. And I want to add custom Visible property that doesn't hide the control when it becomes False on runtime. To change the visibility only when save the changes.

I am using Hide some properties in PropertyGrid at run-time 's solution to show specific properties of the control like {Text, BackColor, ForeColor, Font, Size, Location, Visible} etc.

private void CreateUIEditor(Control c)
{
    if (c is Label)
    {
        propertyGrid.SelectedObject = new CustomObjectWrapper(c, new List<string>() 
        { "Text", "BackColor", "ForeColor", "Font", "Visible"});
    }
    //...
}
public class CustomObjectWrapper : CustomTypeDescriptor
{
    public object WrappedObject { get; private set; }
    public List<string> BrowsableProperties { get; private set; }
    public CustomObjectWrapper(object o, List<string> pList)
        : base(TypeDescriptor.GetProvider(o).GetTypeDescriptor(o))
    {
        WrappedObject = o;
        BrowsableProperties = pList;
    }
    public override PropertyDescriptorCollection GetProperties()
    {
        return this.GetProperties(new Attribute[] { });
    }
    public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
    {
        var properties = base.GetProperties(attributes).Cast<PropertyDescriptor>()
             .Where(p => BrowsableProperties.Contains(p.Name))
             .Select(p => TypeDescriptor.CreateProperty(
                 WrappedObject.GetType(),
                 p,
                 p.Attributes.Cast<Attribute>().ToArray()))
             .ToArray();
        return new PropertyDescriptorCollection(properties);
    }
}

Solution

  • The basic idea here is to have a shadow property which doesn't belong to the original object, but is showing in the PropertyGrid. Such a property can belong to the proxy class itself.

    The following proxy class will hide the original Visible property, however it shows a Visible property which you can change but will not change the visibility of the original object:

    enter image description here

    Here's the code:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Linq;
    public class CustomObjectWrapper : CustomTypeDescriptor
    {
        public object WrappedObject { get; private set; }
        public List<string> BrowsableProperties { get; private set; }
        public CustomObjectWrapper(object o, List<string> pList)
            : base(TypeDescriptor.GetProvider(o).GetTypeDescriptor(o))
        {
            WrappedObject = o;
            BrowsableProperties = pList;
        }
        public override PropertyDescriptorCollection GetProperties()
        {
            return this.GetProperties(new Attribute[] { });
        }
        public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
            var properties = base.GetProperties(attributes).Cast<PropertyDescriptor>()
                 .Where(p => p.Name != "Visible")
                 .Where(p => BrowsableProperties.Contains(p.Name))
                 .Select(p => TypeDescriptor.CreateProperty(
                     WrappedObject.GetType(),
                     p,
                     p.Attributes.Cast<Attribute>().ToArray()))
                 .ToList();
            if (BrowsableProperties.Contains("Visible"))
            {
                var p = TypeDescriptor.GetProperties(this, true)["Visible"];
                properties.Add(TypeDescriptor.CreateProperty(
                    this.GetType(), p, new[] { BrowsableAttribute.Yes }));
            }
            return new PropertyDescriptorCollection(properties.ToArray());
        }
        public bool Visible { get; set; }
        public override object GetPropertyOwner(PropertyDescriptor pd)
        {
            if (pd == null)
                return base.GetPropertyOwner(pd);
            else if (pd.Name == "Visible")
                return this;
            else
                return WrappedObject;
        }
    }