windowsforms-403204-controls-and-libraries-property-grid-row-expandibility.md
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.
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.
The selected object in this example exposes a parent property that contains two child properties. The control automatically creates sub-rows for child properties.
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);
}
}
Imports System
Imports System.ComponentModel
Imports System.Drawing
Imports System.Globalization
propertyGridControl.SelectedObject = New DemoObject()
Public Partial Class DemoObject
Private parentPropertyValue As DemoChildObject = New DemoChildObject()
<Category("Demo")>
Public Property ParentProperty As DemoChildObject
Get
Return parentPropertyValue
End Get
Set(ByVal value As DemoChildObject)
parentPropertyValue = value
End Set
End Property
End Class
<TypeConverter(GetType(DemoChildObjectConverter))>
Public Class DemoChildObject
Private sizeValue As Integer = 1
Private colorValue As Color = Color.Empty
<NotifyParentProperty(True), DefaultValue(1)>
Public Property SizeChildProperty As Integer
Get
Return sizeValue
End Get
Set(ByVal value As Integer)
If sizeValue <> value Then
sizeValue = value
End If
End Set
End Property
<NotifyParentProperty(True)>
<DefaultValue(GetType(Color), "")>
Public Property ColorChildProperty As Color
Get
Return colorValue
End Get
Set(ByVal value As Color)
If colorValue <> value Then
colorValue = value
End If
End Set
End Property
End Class
Public Class DemoChildObjectConverter
Inherits ExpandableObjectConverter
' This method returns a string that is
' displayed in the parent row.
Public Overrides Function ConvertTo(ByVal context As ITypeDescriptorContext, ByVal culture As CultureInfo, ByVal value As Object, ByVal destinationType As Type) As Object
If destinationType Is GetType(String) Then
Dim borderAppearance As DemoChildObject = TryCast(value, DemoChildObject)
Return $"Size: {borderAppearance.SizeChildProperty}, Color: {borderAppearance.ColorChildProperty}"
End If
Return MyBase.ConvertTo(context, culture, value, destinationType)
End Function
End Class