Skip to main content
All docs
V23.2

Row Expandability

  • 3 minutes to read

The selected object can expose properties that contain child properties. That is, a parent property is a reference to an object whose properties should also be displayed in the control.

Property Grid Row Expandability

The control can display child properties as sub-rows that users can expand with a click on the parent row. To allow the control to generate child rows, you should annotate the parent property (or its type) with a type converter that derives from the ExpandableObjectConverter.

Note

If you create rows in the designer, there is no need to use an expandable object converter. At design time, the control generates sub-rows regardless of whether the converter is applied.

Example

The selected object in this example exposes a parent property that contains two child properties. The control automatically creates sub-rows for child properties.

Property Grid Row Expandability

The code below implements an ExpandableObjectConverter descendant to create a custom converter. This converter is then applied to a type that is the type of the selected object’s parent property.

This custom converter overrides the ConvertTo method to convert the parent object to a string. This string is displayed in the parent row. Child properties are annotated with the NotifyParentPropertyAttribute to notify the parent property about changes in a child property. When a child property changes, the string in the parent row is also updated.

using System;
using System.ComponentModel;
using System.Drawing;
using System.Globalization;

propertyGridControl.SelectedObject = new DemoObject();

public partial class DemoObject {
    DemoChildObject parentPropertyValue = new DemoChildObject();

    [Category("Demo")]
    public DemoChildObject ParentProperty {
        get {
            return this.parentPropertyValue;
        }

        set {
            this.parentPropertyValue = value;
        }
    }
}

[TypeConverter(typeof(DemoChildObjectConverter))]
public class DemoChildObject {
    private int sizeValue = 1;
    private Color colorValue = Color.Empty;

    [NotifyParentProperty(true),
    DefaultValue(1)]
    public int SizeChildProperty {
        get {
            return sizeValue;
        }
        set {
            if (sizeValue != value) {
                sizeValue = value;
            }
        }
    }


    [NotifyParentProperty(true)]
    [DefaultValue(typeof(Color), "")]
    public Color ColorChildProperty {
        get {
            return colorValue;
        }
        set {
            if (colorValue != value) {
                colorValue = value;
            }
        }
    }
}

public class DemoChildObjectConverter : ExpandableObjectConverter {
    // This method returns a string that is
    // displayed in the parent row.
    public override object ConvertTo(
        ITypeDescriptorContext context,
        CultureInfo culture,
        object value,
        Type destinationType) {
        if (destinationType == typeof(string)) {
            DemoChildObject borderAppearance = value as DemoChildObject;
            return $"Size: {borderAppearance.SizeChildProperty}, Color: {borderAppearance.ColorChildProperty}";
        }

        return base.ConvertTo(
            context,
            culture,
            value,
            destinationType);
    }
}