Search code examples
c#.netwinformswindows-forms-designerpropertygrid

C# Winform CollectionPropertiesEditor - How to hide some properties in the built-in PropertyGrid based on runtime condition


Is there a way to hide show properties in “CollectionPropertiesEditor’s PropertyGrid” Recently I found out that there is a way to change the PropertyGrid’s Browsable attribute at run time.

I want to know if this can be done to a “CollectionPropertiesEditor’s PropertyGrid”, I have been un-successful at finding relevant results on Google Search. Now I have my hopes on StackOverflow to help me solve this problem.

Problem: I had to add some properties to GridColumn control due to new customer requirements.

    [Category("Extra")]
    [Browsable(true)]
    public int? Position { get; set; }

    [Category("Extra")]
    [Browsable(true)]
    public string storedColumn { get; set; }

What I was hoping to work earlier:

    [Category("Extra")]
    [Browsable(matchSomeRunTimeCondition)]
    public int? Position { get; set; }

    [Category("Extra")]
    [Browsable(matchSomeRunTimeCondition)]
    public string storedColumn { get; set; }

Why it does not work?

Because Browsable Attribute can only accept Constant. And matchSomeRunTimeCondition is not a constant. A user can change it when-ever he wants while the application is still running.

In code if there is a function that I can use to make these invisible at run-time I will be really greatful if someone can help me write one such function or conditional statement like so:

If (property’s category == “Extra”) {

//Do not show this property in the propertygrid.

//Or in other words, make Browasable Attribute False at run time.

}

At compile time I am setting the Browsable property to true because it needs to be visible in some conditions. But I need a mechanism to hide this based on user's choice at runtime.

This problem was overcome in the propertygrid by means of setting it while loading the selected control as explained the post: Make all properties with specific Category name invisible in PropertyGrid in c# Winforms at Runtime based on some condition

However in the CollectionPropertiesEditor that I use to hold my grid columns does not have this luxury (at least I could not find out how to do it).

I store all the grid columns of my grid in the form of list of GridColumns as a property.

This is how I am currently storing the GridColumns in the Grid’s properties:

    [Browsable(true)]
    [Editor(typeof(CollectionPropertiesEditor), typeof(UITypeEditor))]
    public List<TGridColumn> Columns { get; set; }

enter image description here

enter image description here Here I don’t know how to pass my condition to make the aforementioned columns disappear at runtime.


Solution

  • You should write your own type descriptor by deriving from CustomTypeDescriptor or implementing ICustomTypeDescriptor. Here is an example:

    MyPropertyDescriptor

    Provide a custom description for a property. Here I override Attributes property to provide a new list of attributes for property. For example I check if the property has [Category("Extra")], I also added a [Browsable(false)] to its attribute collection.

    using System;
    using System.ComponentModel;
    using System.Linq;
    public class MyPropertyDescriptor : PropertyDescriptor
    {
        PropertyDescriptor o;
        public MyPropertyDescriptor(PropertyDescriptor originalProperty)
            : base(originalProperty) { o = originalProperty; }
        public override bool CanResetValue(object component)
        { return o.CanResetValue(component); }
        public override object GetValue(object component) { return o.GetValue(component); }
        public override void ResetValue(object component) { o.ResetValue(component); }
        public override void SetValue(object component, object value) 
        { o.SetValue(component, value); }
        public override bool ShouldSerializeValue(object component) 
        { return o.ShouldSerializeValue(component); }
        public override AttributeCollection Attributes
        {
            get
            {
                var attributes = base.Attributes.Cast<Attribute>().ToList();
                var category = attributes.OfType<CategoryAttribute>().FirstOrDefault();
                if (category != null && category.Category == "Extra")
                    attributes.Add(new BrowsableAttribute(false));
                return new AttributeCollection(attributes.ToArray());
            }
        }
        public override Type ComponentType { get { return o.ComponentType; } }
        public override bool IsReadOnly { get { return o.IsReadOnly; } }
        public override Type PropertyType { get { return o.PropertyType; } }
    }
    

    MyTypeDescriptor

    Used to provide a list of custom property descriptors for a type.

    using System;
    using System.ComponentModel;
    using System.Linq;
    public class MyTypeDescriptor : CustomTypeDescriptor
    {
        ICustomTypeDescriptor original;
        public MyTypeDescriptor(ICustomTypeDescriptor originalDescriptor)
            : base(originalDescriptor) { original = originalDescriptor; }
        public override PropertyDescriptorCollection GetProperties()
        { return this.GetProperties(new Attribute[] { }); }
        public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
            var properties = base.GetProperties(attributes).Cast<PropertyDescriptor>()
                                 .Select(p => new MyPropertyDescriptor(p))
                                 .ToArray();
            return new PropertyDescriptorCollection(properties);
        }
    }
    

    MyTypeDescriptionProvider

    Used to connect MyTypeDescriptor to a class using TypeDescriptionProvider attribute.

    using System;
    using System.ComponentModel;
    public class MyTypeDescriptionProvider : TypeDescriptionProvider
    {
        public MyTypeDescriptionProvider()
            : base(TypeDescriptor.GetProvider(typeof(object))) { }
    
        public override ICustomTypeDescriptor GetTypeDescriptor(Type type, object o)
        {
            ICustomTypeDescriptor baseDescriptor = base.GetTypeDescriptor(type, o);
            return new MyTypeDescriptor(baseDescriptor);
        }
    }
    

    MySampleClass

    Contains a property decorated with [Category("Extra")]. So Property2 will not be visible in property grid. (In visual studio or collection editor or even run-time property grid)

    [TypeDescriptionProvider(typeof(MyTypeDescriptionProvider))]
    public class MySampleClass
    {
        public int Property1 { get; set; }
        [Category("Extra")]
        public string Property2 { get; set; }
    }
    

    MyComplexComponent

    Contains a collection of MySampleClass. So you can see behavior of MySampleClass in collection editor.

    using System.Collections.ObjectModel;
    using System.ComponentModel;
    public class MyComplexComponent:Component
    {
        public MyComplexComponent()
        {
            MySampleClasses = new Collection<MySampleClass>();
        }
        [DesignerSerializationVisibility(DesignerSerializationVisibility.Content)]
        public Collection<MySampleClass> MySampleClasses { get; set; }
    }
    

    Screenshot

    enter image description here