Search code examples
c#.netvb.netwinformsuitypeeditor

PropertyGrid: remove a property of a custom data type only for a specific property


First: the wording of the question may be inaccurate, sorry for that. The actual question is below all code snippets.
Second: the code is in C#, but I usually write code in VB.NET.

I have a class LabelData which contains data for the visual appearence of a user drawn label. Short example:

public class LabelData
{
    public Color BackColor1 { get; set; }
    public Color BackColor2 { get; set; }
    public Color TextColor { get; set; }
    public int TextAngle { get; set; }
    public string Text { get; set; }
}

My UserControl draws a lot of text (lets call them small labels) using LabelData. It looks like this (simplified):

public class UserControl1
{
    public LabelData Title { get; set; }

    protected override void OnPaint(PaintEventArgs e)
    {
        // draw title here

        LabelData currentLabel;

        for (int i = 0; i <= 9; i++)
        {
            currentLabel = new LabelData();
            currentLabel.BackColor1 = Color.Green;
            currentLabel.BackColor2 = Color.YellowGreen;
            currentLabel.TextAngle = 0;
            currentLabel.Text = "Element" + i.ToString();

            // draw text here
        }
    }
}

All data for the small labels are defined within OnPaint. I don't want this. I thought of a Template for the smaller labels like the DataGridViewRowTemplate. This also allows me to implement ICloneable for LabelData.

This would change to:

public class UserControl1
{
    public LabelData Title { get; set; }
    public LabelData LabelTemplate { get; set; }

    protected override void OnPaint(PaintEventArgs e)
    {
        LabelData currentLabel;

        for (int i = 0; i <= 9; i++)
        {
            currentLabel = LabelTemplate.Clone();
            currentLabel.Text = "Element" + i.ToString();
        }
    }
}

Now the question: How can I remove the Text-Property for the LabelTemplate property (but not for the Title property) in a PropertyGrid since this property changes in OnPaint anyways?

PS: I tried to create a custom designer and overwrite PreFilterProperties to remove the Text property, but I couldn't add a DesignerAttribute to the LabelTemplate property.


Solution

  • Since you probably will need an ExpandableObjectConverter to present properties assigned from your LabelData Class objects, you could create a Custom TypeConverter based on ExpandableObjectConverter and remove Properties from the base Class Object, when you want to present this object differently in specific circumstances.

    Here, as example, while both Title and Template properties are shown as an expandable object in the PropertyGrid (so you can edit each property value with its specific type editor), the Template property has a slightly different Type Converter, where the GetProperties() method is overridden to remove the Text property from the underlying Class object, so it won't be shown in the PropertyGrid:

    Custom ExpandableObjectConverter

    The Template expandable object property doesn't show the Text property as its fellow Title property

    C# version:

    public class UserControl1
    {
        public UserControl1() {
            Template = new LabelData() { ... };
            Title = new LabelData() { ... };
        }
    
        [TypeConverter(typeof(ExpandableObjectConverter))]
        public LabelData Title { get; set; }
    
        [TypeConverter(typeof(CustomExpandableConverter))]
        public LabelData Template { get; set; }
    
        // [...]
    }
    
    public class CustomExpandableConverter : ExpandableObjectConverter
    {
        public CustomExpandableConverter() { }
    
        public override PropertyDescriptorCollection GetProperties(ITypeDescriptorContext context, object value, Attribute[] attributes)
        {
            var props = base.GetProperties(context, value, attributes)
                            .OfType<PropertyDescriptor>().Where(pd => pd.Name != "Text").ToArray();
            return new PropertyDescriptorCollection(props);
        }
    }
    

    VB.Net version:

    Public Class UserControl1
        Public Sub New()
            Template = New LabelData()
            Title = New LabelData()
        End Sub
    
        <TypeConverter(GetType(ExpandableObjectConverter))>
        Public Property Title As LabelData
    
        <TypeConverter(GetType(CustomExpandableConverter))>
        Public Property Template As LabelData
    
        '[...]
    End Class
    
    Public Class CustomExpandableConverter
        Inherits ExpandableObjectConverter
    
        Public Sub New()
        End Sub
    
        Public Overrides Function GetProperties(context As ITypeDescriptorContext, value As Object, attributes() As Attribute) As PropertyDescriptorCollection
            Dim props = MyBase.GetProperties(context, value, attributes).
                               OfType(Of PropertyDescriptor)().
                               Where(Function(pd) Not pd.Name.Equals("Text")).ToArray()
            Return New PropertyDescriptorCollection(props)
        End Function
    End Class