Custom Editor in WPF PropertyGrid

14 Aug 202324 minutes to read

The PropertyGrid control supports several built-in editors. Based on the property type, the built-in editors automatically assigned as value editor for the properties. You can assign own value editor(control) for the properties instead of default value editor by using the Editor attribute or CustomEditorCollection.

For example, if you creates an EmailID property as a string type, TextBox is assigned as a value editor and all the text will be allowed. If you want to accept the input that is only in the mail id format, you can assign SfMaskedEdit control with email-id mask as the value editor for the EmailID property.

Best practice to follow

When using the CustomEditorCollection property to assign a custom editor to multiple properties of the same data type in the PropertyGrid, it is recommended to use the EditorType,PropertyType and HasPropertyType properties of the CustomEditor class. Otherwise, for proper functionality, you must create a new CustomEditor class and assign it to each property using the Properties collection property.

Creating the Custom Editor

To create CustomEditor, we need to implement ITypeEditor interface. Here, SfMaskedEdit control is assigned with mail id mask as EmailEditor and UpDown control is assigned with min, max value as IntegerEditor. EmailEditor and IntegerEditor are the custom editors.

//Custom Editor for the EmailId properties.
public class EmailEditor : ITypeEditor {
    SfMaskedEdit maskededit;
    public void Attach(PropertyViewItem property, PropertyItem info) {
        if (info.CanWrite) {
            var binding = new Binding("Value")
            {
                Mode = BindingMode.TwoWay,
                Source = info,
                ValidatesOnExceptions = true,
                ValidatesOnDataErrors = true
            };
            BindingOperations.SetBinding(maskededit, SfMaskedEdit.ValueProperty, binding);
        }
        else {
            maskededit.IsEnabled = false;
            var binding = new Binding("Value")
            {
                Source = info,
                ValidatesOnExceptions = true,
                ValidatesOnDataErrors = true
            };
            BindingOperations.SetBinding(maskededit, SfMaskedEdit.ValueProperty, binding);
        }
    }
    public object Create(PropertyInfo propertyInfo) {
        maskededit = new SfMaskedEdit();
        maskededit.MaskType = MaskType.RegEx;
        maskededit.Mask = "[A-Za-z0-9._%-]+@[A-Za-z0-9]+.[A-Za-z]{2,3}";
        return maskededit;
    }
    public void Detach(PropertyViewItem property) {

    }
}

//Custom Editor for the integer type properties.
public class IntegerEditor : ITypeEditor {
    UpDown upDown;
    public void Attach(PropertyViewItem property, PropertyItem info) {
        if (info.CanWrite) {
            var binding = new Binding("Value")
            {
                Mode = BindingMode.TwoWay,
                Source = info,
                ValidatesOnExceptions = true,
                ValidatesOnDataErrors = true
            };
            BindingOperations.SetBinding(upDown, UpDown.ValueProperty, binding);
        }
        else {
            upDown.IsEnabled = false;
            var binding = new Binding("Value")
            {
                Source = info,
                ValidatesOnExceptions = true,
                ValidatesOnDataErrors = true
            };
            BindingOperations.SetBinding(upDown, UpDown.ValueProperty, binding);
        }
    }
    public object Create(PropertyInfo propertyInfo) {
        upDown = new UpDown();
        upDown.ApplyZeroColor = false;
        upDown.MinValue = 0;
        upDown.MaxValue = 100;
        upDown.NumberDecimalDigits = 0;
        return upDown;
    }
    public void Detach(PropertyViewItem property) {

    }
}

NOTE

To assign a created custom editor to a properties, refer the Assigning a Custom Editor topic.

Creating Custom Editor for a dynamic property.

If the SelectedObject has a property of type dynamic, ExpandoObject or ICustomTypeDescriptor, we can create CustomEditor class by inheriting BaseTypeEditor. You can initialize a new instance of the custom editor using the BaseTypeEditor.Create(PropertyDescriptor) function. Below example shows, how to get the value of dynamic properties using its PropertyDescriptor and apply the value in ComboEditor to ComboBox objects.

//Custom Editor for the List<string> type properties.
public class ComboEditor : BaseTypeEditor
{
    ComboBox comboBox;
      
    public override void Attach(PropertyViewItem property, PropertyItem info)
    {
        var binding = new Binding("Value")
        {
            Mode = BindingMode.TwoWay,
            Source = info,
            ValidatesOnExceptions = true,
            ValidatesOnDataErrors = true
        };
        BindingOperations.SetBinding(comboBox, ComboBox.SelectedItemProperty, binding);
    }

    // Create a custom editor for a normal property
    public override object Create(PropertyInfo PropertyInfo)
    {
        throw new NotImplementedException();
    }
    
    // Create a custom editor for a dynamic property
    public override object Create(PropertyDescriptor PropertyDescriptor)
    {
        comboBox = new ComboBox();
        // Getting the values of dynamic property
        dynamic comboBoxItemsList = PropertyDescriptor.GetValue(PropertyDescriptor.Name);
        foreach (dynamic items in comboBoxItemsList)
        {
            comboBox.Items.Add(items);
        }

        comboBox.SelectedIndex = 0;
        return comboBox;
    }

    public override void Detach(PropertyViewItem property)
    {
        comboBox = null;
    }
}

NOTE

Download demo application from GitHub.

Assigning a Custom Editor using Editor Attribute

We can assign the CustomEditor to any individual property by name of the property and to multiple properties based on the property type by using the Editor attribute.

//CustomEditor for the specfic(EmailID) property
[Editor("EmailID", typeof(EmailEditor))]

//Custom Editor for the multiple(Tnteger type) properties
[Editor(typeof(int), typeof(IntegerEditor))]
public class Employee {
    public string EmailID { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public int Experience { get; set; }
}

class ViewModel {
    public object SelectedEmployee { get; set; }
    public ViewModel() {
        SelectedEmployee = new Employee()
        {
            Age = 25,
            Name = "mark",
            Experience = 5,
            EmailID = "mark@gt"
        };
    }
}
<syncfusion:PropertyGrid SelectedObject="{Binding SelectedEmployee}"
                         x:Name="propertyGrid1" >
    <syncfusion:PropertyGrid.DataContext>
        <local:ViewModel></local:ViewModel>
   </syncfusion:PropertyGrid.DataContext>
</syncfusion:PropertyGrid>
PropertyGrid propertyGrid1 = new PropertyGrid();
propertyGrid1.DataContext = new ViewModel();
propertyGrid1.SetBinding(PropertyGrid.SelectedObjectProperty, new Binding("SelectedEmployee"));

Here, EmailID property value editor changed from TextBox to MaskedEdit control with email id mask. Also, we assigned the IntegerEditor for the integer type properties, so it applied to the Experience and Age properties. Then, the value editors for the Experience and Age property is changed from NumericTextBox to Updown control.

Property grid with specified custom editors

Assigning a Custom Editor using Collection

We can assign the CustomEditor to any particular property and to multiple properties using the CustomEditorCollection.

Assigning a Custom Editor to the specific property

If we want to apply custom editor for any particular property, we need to create the CustomEditor instance, assign our own editor to the CustomEditor.Editor and add the property name to the CustomEditor.Properties collection. Then, add the CustomEditor instance to the PropertyGrid.CustomEditorCollection.

public class Employee {
    public string EmailID { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public int Experience { get; set; }
}

class ViewModel {
    public object SelectedEmployee { get; set; }
    public CustomEditorCollection customEditorCollection = new CustomEditorCollection();
    public CustomEditorCollection CustomEditorCollection
    {
        get { return customEditorCollection; }
        set { customEditorCollection = value; }
    }
    public ViewModel() {
        SelectedEmployee = new Employee() { Age = 25, Name = "mark", Experience = 5, EmailID = "mark@gt" };

        // EmailEditor added to the collection and will applied to the "EmailID" property
        CustomEditor editor1 = new CustomEditor();
        editor1.Editor = new EmailEditor();
        editor1.Properties.Add("EmailID");
        CustomEditorCollection.Add(editor1);
    }
}
<syncfusion:PropertyGrid CustomEditorCollection="{Binding CustomEditorCollection}" 
                         SelectedObject="{Binding SelectedEmployee}"
                         x:Name="propertyGrid1" >
    <syncfusion:PropertyGrid.DataContext>
        <local:ViewModel></local:ViewModel>
   </syncfusion:PropertyGrid.DataContext>
</syncfusion:PropertyGrid>
PropertyGrid propertyGrid1 = new PropertyGrid();
propertyGrid1.DataContext = new ViewModel();
propertyGrid1.SetBinding(PropertyGrid.CustomEditorCollectionProperty, new Binding("CustomEditorCollection"));
propertyGrid1.SetBinding(PropertyGrid.SelectedObjectProperty, new Binding("SelectedEmployee"));

Here, The EmailID is a string property, the TextBox is assigned as a default editor. We changed it as SfMaskedEdit textbox that accepts only inputs which are in the email id format.

CustomEditor applied for EmailID property

Assigning a Custom Editor based on the property type

If we want to apply custom editor for multiple properties which are all contains same type, we need to create the CustomEditor instance, assign our own editor to the CustomEditor.Editor and sets the CustomEditor.HasPropertyType property to true. Then, mention the property type to the CustomEditor.PropertyType.

public class Employee {
    public string EmailID { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public int Experience { get; set; }
}

class ViewModel {
    public object SelectedEmployee { get; set; }
    public CustomEditorCollection customEditorCollection = new CustomEditorCollection();
    public CustomEditorCollection CustomEditorCollection
    {
        get { return customEditorCollection; }
        set { customEditorCollection = value; }
    }
    public ViewModel() {
        SelectedEmployee = new Employee() { Age = 25, Name = "mark", Experience = 5, EmailID = "mark@gt" };

        // IntegerEditor added to the collection and will applied to the "int" type properties
        CustomEditor editor = new CustomEditor();
        editor.Editor = new IntegerEditor();
        editor.HasPropertyType = true;
        editor.PropertyType = typeof(int);
        CustomEditorCollection.Add(editor);
    }
}
<syncfusion:PropertyGrid CustomEditorCollection="{Binding CustomEditorCollection}" 
                         SelectedObject="{Binding SelectedEmployee}"
                         x:Name="propertyGrid1" >
    <syncfusion:PropertyGrid.DataContext>
        <local:ViewModel></local:ViewModel>
   </syncfusion:PropertyGrid.DataContext>
</syncfusion:PropertyGrid>
PropertyGrid propertyGrid1 = new PropertyGrid();
propertyGrid1.DataContext = new ViewModel();
propertyGrid1.SetBinding(PropertyGrid.CustomEditorCollectionProperty, new Binding("CustomEditorCollection"));
propertyGrid1.SetBinding(PropertyGrid.SelectedObjectProperty, new Binding("SelectedEmployee"));

CustomEditor applied for integer type properties

Here, we assigned the IntegerEditor custom editor for the integer type properties, so it applied to the Experience and Age properties. Then, the value editors for the Experience and Age property is changed from NumericTextBox to Updown control.

Click here to download the sample that showcases the CustomEditor support.

Assigning a Custom Editor by the editor type

By default, when we use the CustomEditor.Editor to target multiple properties in PropertyGrid, the same custom editor instance is used for all those properties. To maintain a dedicated or separate instance of the custom editor for each property, use CustomEditor.EditorType. The default value of the EditorType property is null.

You can set the value for EditorType property when custom editor is initialized in ViewModel class as shown below.

<syncfusion:PropertyGrid CustomEditorCollection="{Binding CustomEditorCollection}" 
                         SelectedObject="{Binding SelectedEmployee}"
                         x:Name="propertyGrid1" >
    <syncfusion:PropertyGrid.DataContext>
        <local:ViewModel></local:ViewModel>
   </syncfusion:PropertyGrid.DataContext>
</syncfusion:PropertyGrid>
public class Employee
{
    public string Country { get; set; }
    public double Experience { get; set; }
    public string Name { get; set; }
    public double Age { get; set; }
}

class ViewModel 
{
    public object SelectedEmployee { get; set; }

    private CustomEditorCollection customEditorCollection = new CustomEditorCollection();
    public CustomEditorCollection CustomEditorCollection
    {
        get { return customEditorCollection; }
        set { customEditorCollection = value; }
    }

    public ViewModel()
    {
         SelectedEmployee = new Employee() { Age = 25, Name = "mark", Experience = 5, EmailID = "mark@gt.com" };
        CustomEditor editor1 = new CustomEditor()
        {
            EditorType = typeof(IntegerEditor),
            HasPropertyType = true,
            PropertyType = typeof(double)
        };

        CustomEditorCollection.Add(editor1);

    }
}

You can also set value for CustomEditorType property for CustomEditor class in the xaml file as shown below.

CustomEditor applied for integer type properties

Use constructor with parameters in custom editor

By default, PropertyGrid control only invokes the constructor without parameter in custom editor. You can invoke and pass arguments to the constructors with any number of parameters in custom editor using the ConstructorParameter property of CustomEditor class. The default value of ConstructorParameter is null. This can be achieved by following the below steps.

NOTE

ConstructorParameter property will work only if a value is assigned for the EditorType property in the CustomEditor class.

  1. Create a custom editor for the desired property item in PropertyGrid. Add constructor with parameters in the custom editor class.

    public class IntegerEditor : ITypeEditor
    {
        IntegerTextBox integerTextBox;
        bool ShowUpDown = false;
       
        public IntegerEditor()
        {
       
        }
       
        // Constructor with parameter in custom editor
        public IntegerEditor(bool showUpDown)
        {
            ShowUpDown = showUpDown;
        }
       
        public void Attach(PropertyViewItem property, PropertyItem info)
        {
            if (info.CanWrite)
            {
                var binding = new Binding("Value")
                {
                    Mode = BindingMode.TwoWay,
                    Source = info,
                    ValidatesOnExceptions = true,
                    ValidatesOnDataErrors = true
                };
                BindingOperations.SetBinding(integerTextBox, IntegerTextBox.ValueProperty, binding);
            }
            else
            {
                integerTextBox.IsEnabled = false;
                var binding = new Binding("Value")
                {
                    Source = info,
                    ValidatesOnExceptions = true,
                    ValidatesOnDataErrors = true
                };
                BindingOperations.SetBinding(integerTextBox, IntegerTextBox.ValueProperty, binding);
            }
        }
        public object Create(PropertyInfo propertyInfo)
        {
            integerTextBox = new IntegerTextBox()
            {
                ApplyZeroColor = false,
                MinValue = 0,
                MaxValue = 50,
                ShowSpinButton = ShowUpDown,
            };
            return integerTextBox;
        }
        public void Detach(PropertyViewItem property)
        {
       
        }
    }
  2. Create Employee and ViewModel classes with required properties. Pass the required objects in an object array and assign it to the ConstructorParameter property of CustomEditor as shown below.

    public class Employee
    {
        public string EmailID { get; set; }
        public double Experience { get; set; }
        public string Name { get; set; }
        public long Age { get; set; }
    }
       
       
    class ViewModel 
    {
        public object SelectedEmployee { get; set; }
       
        private CustomEditorCollection customEditorCollection = new CustomEditorCollection();
        public CustomEditorCollection CustomEditorCollection
        {
            get { return customEditorCollection; }
            set { customEditorCollection = value; }
        }
       
        public int DecimalDigits { get; set; }
        public ViewModel()
        {
            SelectedEmployee = new Employee() { Age = 25, Name = "Mark Anthony", EmailID = "markanthony@syncfusion.com", Experience = 3 };
            CustomEditor editor1 = new CustomEditor()
            {
                EditorType = typeof(IntegerEditor),
                HasPropertyType = true,
                PropertyType = typeof(long),
                ConstructorParameters = new object [] {true}
            };
       
            CustomEditorCollection.Add(editor1);
       
        }
    }
    <syncfusion:PropertyGrid CustomEditorCollection="{Binding CustomEditorCollection}" SelectedObject="{Binding SelectedEmployee}" 
                             x:Name="propertyGrid1" >
        <syncfusion:PropertyGrid.DataContext>
            <local:ViewModel></local:ViewModel>
        </syncfusion:PropertyGrid.DataContext>
    </syncfusion:PropertyGrid>

Since we have assigned the custom editor for property type long(Int64), the custom editor will be applied for Age property item. You can also create custom editor and set value for ConstructorParameter property in xaml file as shown below.

<syncfusion:PropertyGrid SelectedObject="{Binding SelectedEmployee}" 
                            x:Name="propertyGrid1" >
    <syncfusion:PropertyGrid.DataContext>
        <local:ViewModel></local:ViewModel>
    </syncfusion:PropertyGrid.DataContext>
    <syncfusion:PropertyGrid.CustomEditorCollection>
        <syncfusion:CustomEditorCollection>
            <syncfusion:CustomEditor  PropertyType="{x:Type a:Int64}" HasPropertyType="True" EditorType="{x:Type local:IntegerEditor}" >
                <syncfusion:CustomEditor.ConstructorParameters>
                    <x:Array Type="{x:Type a:Object}">
                        <a:Boolean>True</a:Boolean>
                    </x:Array>
                </syncfusion:CustomEditor.ConstructorParameters>
            </syncfusion:CustomEditor>
        </syncfusion:CustomEditorCollection>
    </syncfusion:PropertyGrid.CustomEditorCollection>
</syncfusion:PropertyGrid>

ConstructorParameter passed in CustomEditor

Create custom editor control for the property item

The PropertyGrid control allows users to create their editor controls using the CustomEditor class and CustomEditorCollection property for property items. The custom editors can be inherited from ITypeEditor and BaseTypeEditor interfaces.

The Create method in the custom editor class can be used to create and assign the required editor controls for specific property items. The Create method’s PropertyInfo parameter allows users to customize the editor control based on the value of PropertyInfo class.

public object Create(PropertyInfo propertyInfo)
{
    textBox = new IntegerTextBox();
    if (propertyInfo.Name == "Age")
    {
        textBox.MinValue = 20;
        textBox.MaxValue = 50;
    }
    if (propertyInfo.CanWrite)
        textBox.ShowSpinButton = true;
    
    return textBox;
}

WPF PropertyGrid custom editor Create method

NOTE

If a CustomEditor class is inherited from BaseTypeEditor interface, the Create method will have PropertyDescriptor parameter value which can be used as per requirement.

Attach the custom editor with property item

The PropertyGrid control control allows users to bind the essential properties of the custom editor control with the properties of the property items using the Attach method. You can also customize the property items using the PropertyItem and PropertyViewItem using the parameters in the Attach method.

public void Attach(PropertyViewItem property, PropertyItem info)
{
    if (info.CanWrite)
    {
        property.FontFamily = new FontFamily("Comic Sans MS");
        var binding = new Binding("Value")
        {
            Mode = BindingMode.TwoWay,
            Source = info,
            ValidatesOnExceptions = true,
            ValidatesOnDataErrors = true
        };
        BindingOperations.SetBinding(textBox, IntegerTextBox.ValueProperty, binding);
    }
}

WPF PropertyGrid custom editor Attach method

Dispose the custom editor

The PropertyGrid control control allows you to dispose the custom editor control and it’s dependent properties in CustomEditor class by using the Detach method. You can also dispose the PropertyViewItem which will be available from the parameter of Detach method.

public void Detach(PropertyViewItem property)
{
    if (property != null && this.textBox != null)
    {
        this.textBox = null;
        property.Dispose();
        property = null;
    }
}