Edit this page

Create Custom Editor with RadGridView

The purpose of this tutorial is to show you how to create a custom editor with RadGridView. If you need a custom editor to edit specific data, you can use one of the following approaches:

  • Use the CellEditTemplate property of GridViewColumn.

  • Create a custom column by inheriting from GridViewBoundColumnBase.

Both approaches have some advantages and disadvantages. Although it is quite easy to implement the first option, you cannot easily apply this to all RadGridView instances in your application, and more importantly - this bypasses RadGridView's validation and editing engine.

This tutorial will demonstrate you the second approach by creating a column with an embedded color picker control as an editor.

  • The first step is to create a class that inherits from GridViewBoundColumnBase (this is the base class used to create a column with editing capabilities). Name the class RadColorPickerColumn.

[C#] Example 1: Creating the RadColorPickerColumn class

public class RadColorPickerColumn : GridViewBoundColumnBase
{
}

[VB.NET] Example 1: Creating the RadColorPickerColumn class

Public Class RadColorPickerColumn
    Inherits GridViewBoundColumnBase

    Public Overrides Function CreateCellElement(ByVal cell As GridViewCell, ByVal dataItem As Object) As FrameworkElement
        Dim cellElement As New Border()
        Dim valueBinding = New System.Windows.Data.Binding(Me.DataMemberBinding.Path.Path) With {
            .Mode = BindingMode.OneTime,
            .Converter = New ColorToBrushConverter()
        }
        cellElement.SetBinding(Border.BackgroundProperty, valueBinding)
        cellElement.Width = 45
        cellElement.Height = 20
        cellElement.CornerRadius = New CornerRadius(5)
        Return cellElement
    End Function
    Public Overrides Function CreateCellEditElement(ByVal cell As GridViewCell, ByVal dataItem As Object) As FrameworkElement
        Dim cellEditElement = New RadColorPicker()
        Me.BindingTarget = RadColorPicker.SelectedColorProperty
        cellEditElement.MainPalette = Me.MainPalette
        Dim valueBinding As System.Windows.Data.Binding = Me.CreateValueBinding()
        cellEditElement.SetBinding(RadColorPicker.SelectedColorProperty, valueBinding)
        Return TryCast(cellEditElement, FrameworkElement)
    End Function
    Private Function CreateValueBinding() As System.Windows.Data.Binding
        Dim valueBinding As New System.Windows.Data.Binding()
        valueBinding.Mode = BindingMode.TwoWay
        valueBinding.NotifyOnValidationError = True
        valueBinding.ValidatesOnExceptions = True
        valueBinding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit
        valueBinding.Path = New PropertyPath(Me.DataMemberBinding.Path.Path)
        Return valueBinding
    End Function
    Public Overrides Sub CopyPropertiesFrom(ByVal source As Telerik.Windows.Controls.GridViewColumn)
        MyBase.CopyPropertiesFrom(source)
        Dim _radColorPickerColumn = TryCast(source, RadColorPickerColumn)
        If _radColorPickerColumn IsNot Nothing Then
            Me.MainPalette = _radColorPickerColumn.MainPalette
        End If
    End Sub
    Public Property MainPalette() As ColorPreset
        Get
            Return CType(GetValue(MainPaletteProperty), ColorPreset)
        End Get
        Set(ByVal value As ColorPreset)
            SetValue(MainPaletteProperty, value)
        End Set
    End Property
    Public Shared ReadOnly MainPaletteProperty As DependencyProperty = DependencyProperty.Register("MainPalette", GetType(ColorPreset), GetType(RadColorPickerColumn), New PropertyMetadata(Nothing))
End Class

There are several methods you should override:

  • CreateCellElement() – override this method if you want to customize how cells that belongs to this column will look like. This method is called when GridViewCell is prepared and returned element will be used as a ContentPresenter. If you do not override this method a TextBlock control will be used as a default presenter.

[C#] Example 2: Overriding the CreateCellElement method

public class RadColorPickerColumn : GridViewBoundColumnBase
{
    public override FrameworkElement CreateCellElement(GridViewCell cell, object dataItem)
    {
        Border cellElement = new Border();
        var valueBinding = new System.Windows.Data.Binding(this.DataMemberBinding.Path.Path)
        {
            Mode = BindingMode.OneTime,
            Converter = new ColorToBrushConverter()
        };
        cellElement.SetBinding(Border.BackgroundProperty, valueBinding);
        cellElement.Width = 45;
        cellElement.Height = 20;
        cellElement.CornerRadius = new CornerRadius(5);
        return cellElement;
    }
}

[VB.NET] Example 2: Overriding the CreateCellElement method

Public Class RadColorPickerColumn
    Inherits GridViewBoundColumnBase

    Public Overrides Function CreateCellElement(ByVal cell As GridViewCell, ByVal dataItem As Object) As FrameworkElement
        Dim cellElement As New Border()
        Dim valueBinding = New System.Windows.Data.Binding(Me.DataMemberBinding.Path.Path) With {
            .Mode = BindingMode.OneTime,
            .Converter = New ColorToBrushConverter()
        }
        cellElement.SetBinding(Border.BackgroundProperty, valueBinding)
        cellElement.Width = 45
        cellElement.Height = 20
        cellElement.CornerRadius = New CornerRadius(5)
        Return cellElement
    End Function
End Class

You should note two things here: first, a border with bound background to the color from data item is created and second, a custom converter is used. The next code snippet shows you the code for the ColorToBrushConverter.

[C#] Example 3: The ColorToBrushConverter class

public class ColorToBrushConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var color = (Color)value;
        if (color != null)
        {
            return new SolidColorBrush(color);
        }
        return value;
    }
    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value;
    }
}

[VB.NET] Example 3: The ColorToBrushConverter class

Public Class ColorToBrushConverter
    Implements IValueConverter

    Public Function Convert(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.Convert
        Dim color = DirectCast(value, Color?)
        If color IsNot Nothing Then
            Return New SolidColorBrush(color)
        End If
        Return value
    End Function
    Public Function ConvertBack(ByVal value As Object, ByVal targetType As Type, ByVal parameter As Object, ByVal culture As System.Globalization.CultureInfo) As Object Implements IValueConverter.ConvertBack
        Return value
    End Function
End Class
  • CreateCellEditElement() – override this method to create custom editor element (according to type of the property or some other business logic). This method must be overridden otherwise GridViewCell will have no content when enters into edit mode.
  • [C#] Example 4: Overriding the CreateCellEditElement method

    public override FrameworkElement CreateCellEditElement(GridViewCell cell, object dataItem)
    {
    var cellEditElement = new RadColorPicker();

    this.BindingTarget = RadColorPicker.SelectedColorProperty;
    
    cellEditElement.MainPalette = this.MainPalette;
    
    Binding valueBinding = this.CreateValueBinding();
    
    cellEditElement.SetBinding(RadColorPicker.SelectedColorProperty, valueBinding);
    
    return cellEditElement as FrameworkElement;
    

    }

[VB.NET] Example 4: Overriding the CreateCellEditElement method

Public Overrides Function CreateCellEditElement(ByVal cell As GridViewCell, ByVal dataItem As Object) As FrameworkElement
    Dim cellEditElement = New RadColorPicker()

    Me.BindingTarget = RadColorPicker.SelectedColorProperty

    cellEditElement.MainPalette = Me.MainPalette

    Dim valueBinding As Binding = Me.CreateValueBinding()

    cellEditElement.SetBinding(RadColorPicker.SelectedColorProperty, valueBinding)

    Return TryCast(cellEditElement, FrameworkElement)
End Function

In this method an instance of a RadColorPicker control is created and returned. In order to work properly as an editor you have to bind this editor to the underlying data property. This is done in the CreateValueBinding() method:

[C#] Example 5: The CreateValueBinding() method

private Binding CreateValueBinding()
{
    Binding valueBinding = new Binding();
    valueBinding.Mode = BindingMode.TwoWay;
    valueBinding.NotifyOnValidationError = true;
    valueBinding.ValidatesOnExceptions = true;
    valueBinding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit;
    valueBinding.Path = new PropertyPath(this.DataMemberBinding.Path.Path);
    return valueBinding;
}

[VB.NET] Example 5: The CreateValueBinding() method

Private Function CreateValueBinding() As Binding
    Dim valueBinding As New Binding()
    valueBinding.Mode = BindingMode.TwoWay
    valueBinding.NotifyOnValidationError = True
    valueBinding.ValidatesOnExceptions = True
    valueBinding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit
    valueBinding.Path = New PropertyPath(Me.DataMemberBinding.Path.Path)
    Return valueBinding
End Function

You should set BindingMode to TwoWay, because this is an editor and you need to update data property which is bound to the parent GridViewColumn. The NotifyOnValidationError and ValidatesOnExceptions properties are related to validation engine (if any error or exception occurs while you set new value to the data object will result as validation error and editor will enter into invalid state (if editor has such state)).
Set UpdateSourceTrigger to Explicit and allow RadGridView to validate and update the value of the data item at the right moment. Of course every TwoWay binding requires a path, so you take the path from the DataMemberBinding property.

Another interesting line of the CreateCellEditElement() method is: cellEditElement.MainPalette = this.MainPalette. This line shows how you can transfer properties from column to the actual editor. In order to allow such properties and transfer them to custom column instance you have to override CopyPropertiesFrom() method.

  • CopyPropertiesFrom()

[C#] Example 6: Overriding the CopyPropertiesFrom method

public override void CopyPropertiesFrom(Telerik.Windows.Controls.GridViewColumn source)
{
    base.CopyPropertiesFrom(source);
    var radColorPickerColumn = source as RadColorPickerColumn;
    if (radColorPickerColumn != null)
    {
        this.MainPalette = radColorPickerColumn.MainPalette;
    }
}

public ColorPreset MainPalette
{
    get
    {
        return (ColorPreset)GetValue(MainPaletteProperty);
    }
    set
    {
        SetValue(MainPaletteProperty, value);
    }
}

public static readonly DependencyProperty MainPaletteProperty = DependencyProperty.Register("MainPalette",
    typeof(ColorPreset),
    typeof(RadColorPickerColumn),
    new PropertyMetadata(null));

[VB.NET] Example 6: Overriding the CopyPropertiesFrom method

Public Overrides Sub CopyPropertiesFrom(ByVal source As Telerik.Windows.Controls.GridViewColumn)
    MyBase.CopyPropertiesFrom(source)
    Dim _radColorPickerColumn = TryCast(source, RadColorPickerColumn)
    If _radColorPickerColumn IsNot Nothing Then
        Me.MainPalette = _radColorPickerColumn.MainPalette
    End If
End Sub

Public Property MainPalette() As ColorPreset
    Get
        Return CType(GetValue(MainPaletteProperty), ColorPreset)
    End Get
    Set(ByVal value As ColorPreset)
        SetValue(MainPaletteProperty, value)
    End Set
End Property

Public Shared ReadOnly MainPaletteProperty As DependencyProperty = DependencyProperty.Register("MainPalette", GetType(ColorPreset), GetType(RadColorPickerColumn), New PropertyMetadata(Nothing))

Here is the full code for the RadColorPickerColumn class:

[C#] Example 7: The final RadColorPickerColumn class

public class RadColorPickerColumn : GridViewBoundColumnBase
{
    public override FrameworkElement CreateCellElement(GridViewCell cell, object dataItem)
    {
        Border cellElement = new Border();
        var valueBinding = new System.Windows.Data.Binding(this.DataMemberBinding.Path.Path)
        {
            Mode = BindingMode.OneTime,
            Converter = new ColorToBrushConverter()
        };
        cellElement.SetBinding(Border.BackgroundProperty, valueBinding);
        cellElement.Width = 45;
        cellElement.Height = 20;
        cellElement.CornerRadius = new CornerRadius(5);
        return cellElement;
    }
    public override FrameworkElement CreateCellEditElement(GridViewCell cell, object dataItem)
    {
        var cellEditElement = new RadColorPicker();
        this.BindingTarget = RadColorPicker.SelectedColorProperty;
        cellEditElement.MainPalette = this.MainPalette;
        System.Windows.Data.Binding valueBinding = this.CreateValueBinding();
        cellEditElement.SetBinding(RadColorPicker.SelectedColorProperty, valueBinding);
        return cellEditElement as FrameworkElement;
    }
    private System.Windows.Data.Binding CreateValueBinding()
    {
        System.Windows.Data.Binding valueBinding = new System.Windows.Data.Binding();
        valueBinding.Mode = BindingMode.TwoWay;
        valueBinding.NotifyOnValidationError = true;
        valueBinding.ValidatesOnExceptions = true;
        valueBinding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit;
        valueBinding.Path = new PropertyPath(this.DataMemberBinding.Path.Path);
        return valueBinding;
    }
    public override void CopyPropertiesFrom(Telerik.Windows.Controls.GridViewColumn source)
    {
        base.CopyPropertiesFrom(source);
        var radColorPickerColumn = source as RadColorPickerColumn;
        if (radColorPickerColumn != null)
        {
            this.MainPalette = radColorPickerColumn.MainPalette;
        }
    }
    public ColorPreset MainPalette
    {
        get
        {
            return (ColorPreset)GetValue(MainPaletteProperty);
        }
        set
        {
            SetValue(MainPaletteProperty, value);
        }
    }
    public static readonly DependencyProperty MainPaletteProperty = DependencyProperty.Register("MainPalette",
        typeof(ColorPreset),
        typeof(RadColorPickerColumn),
        new PropertyMetadata(null));
}

[VB.NET] Example 7: The final RadColorPickerColumn class

Public Class RadColorPickerColumn
    Inherits GridViewBoundColumnBase

    Public Overrides Function CreateCellElement(ByVal cell As GridViewCell, ByVal dataItem As Object) As FrameworkElement
        Dim cellElement As New Border()
        Dim valueBinding = New System.Windows.Data.Binding(Me.DataMemberBinding.Path.Path) With {
            .Mode = BindingMode.OneTime,
            .Converter = New ColorToBrushConverter()
        }
        cellElement.SetBinding(Border.BackgroundProperty, valueBinding)
        cellElement.Width = 45
        cellElement.Height = 20
        cellElement.CornerRadius = New CornerRadius(5)
        Return cellElement
    End Function
    Public Overrides Function CreateCellEditElement(ByVal cell As GridViewCell, ByVal dataItem As Object) As FrameworkElement
        Dim cellEditElement = New RadColorPicker()
        Me.BindingTarget = RadColorPicker.SelectedColorProperty
        cellEditElement.MainPalette = Me.MainPalette
        Dim valueBinding As System.Windows.Data.Binding = Me.CreateValueBinding()
        cellEditElement.SetBinding(RadColorPicker.SelectedColorProperty, valueBinding)
        Return TryCast(cellEditElement, FrameworkElement)
    End Function
    Private Function CreateValueBinding() As System.Windows.Data.Binding
        Dim valueBinding As New System.Windows.Data.Binding()
        valueBinding.Mode = BindingMode.TwoWay
        valueBinding.NotifyOnValidationError = True
        valueBinding.ValidatesOnExceptions = True
        valueBinding.UpdateSourceTrigger = UpdateSourceTrigger.Explicit
        valueBinding.Path = New PropertyPath(Me.DataMemberBinding.Path.Path)
        Return valueBinding
    End Function
    Public Overrides Sub CopyPropertiesFrom(ByVal source As Telerik.Windows.Controls.GridViewColumn)
        MyBase.CopyPropertiesFrom(source)
        Dim _radColorPickerColumn = TryCast(source, RadColorPickerColumn)
        If _radColorPickerColumn IsNot Nothing Then
            Me.MainPalette = _radColorPickerColumn.MainPalette
        End If
    End Sub
    Public Property MainPalette() As ColorPreset
        Get
            Return CType(GetValue(MainPaletteProperty), ColorPreset)
        End Get
        Set(ByVal value As ColorPreset)
            SetValue(MainPaletteProperty, value)
        End Set
    End Property
    Public Shared ReadOnly MainPaletteProperty As DependencyProperty = DependencyProperty.Register("MainPalette", GetType(ColorPreset), GetType(RadColorPickerColumn), New PropertyMetadata(Nothing))
End Class
  • Use the just created custom column in XAML like the code below:

[XAML] Example 8: Defining a RadColorPickerColumn in XAML

<Grid x:Name="LayoutRoot" Background="White">

    <Grid.Resources>
        <local:ColorToBrushConverter x:Key="colorToBrushConverter" />
    </Grid.Resources>

    <telerik:RadGridView x:Name="radGridView" AutoGenerateColumns="False">
        <telerik:RadGridView.Columns>
            <telerik:GridViewDataColumn UniqueName="FirstName" DataMemberBinding="{Binding FirstName}" Header="FirstName" />
            <telerik:GridViewDataColumn UniqueName="LastName" DataMemberBinding="{Binding LastName}" Header="LastName" />
            <telerik:GridViewDataColumn UniqueName="Age" DataMemberBinding="{Binding Age}" Header="Age" />

            <local:RadColorPickerColumn UniqueName="FavouriteColor" DataMemberBinding="{Binding FavouriteColor}" 
                                Header="FavouriteColor" MainPalette="ReallyWebSafe"/>

        </telerik:RadGridView.Columns>
    </telerik:RadGridView>

</Grid>

The final result should be similar to the image below:

Figure 1: The RadColorPickerColumn

The RadColorPickerColumn

Integrating the RadColorPickerColumn into the Validation and Editing Engine

In order to integrate the RadColorPickerColumn into RadGridView's validation and editing engine, you should override two additional methods:

  • UpdateSourceWithEditorValue()

[C#] Example 9: Overriding the UpdateSourceWithEditorValue method

public override IList<string> UpdateSourceWithEditorValue(GridViewCell gridViewCell)
{
    List<String> errors = new List<String>();
    RadColorPicker editor = gridViewCell.GetEditingElement() as RadColorPicker;
    BindingExpression bindingExpression = editor.ReadLocalValue(RadColorPicker.SelectedColorProperty) as BindingExpression;
    if (bindingExpression != null)
    {
        bindingExpression.UpdateSource();
        foreach (ValidationError error in Validation.GetErrors(editor))
        {
            errors.Add(error.ErrorContent.ToString());
        }
    }
    return errors.ToList();
}

[VB.NET] Example 9: Overriding the UpdateSourceWithEditorValue method

Public Overrides Function UpdateSourceWithEditorValue(ByVal gridViewCell As GridViewCell) As IList(Of String)
    Dim errors As New List(Of String)()
    Dim editor As RadColorPicker = TryCast(gridViewCell.GetEditingElement(), RadColorPicker)
    Dim bindingExpression As BindingExpression = TryCast(editor.ReadLocalValue(RadColorPicker.SelectedColorProperty), BindingExpression)
    If bindingExpression IsNot Nothing Then
        bindingExpression.UpdateSource()
        For Each [error] As ValidationError In Validation.GetErrors(editor)
            errors.Add([error].ErrorContent.ToString())
        Next [error]
    End If
    Return errors.ToList()
End Function
  • GetNewValueFromEditor()

[C#] Example 10: Overriding the GetNewValueFromEditor method

public override object GetNewValueFromEditor(object editor)
{
    RadColorPicker colorPicker = editor as RadColorPicker;
    if (colorPicker != null)
    {
        return colorPicker.SelectedColor;
    }
    else
    {
        return null;
    }
}

[VB.NET] Example 10: Overriding the GetNewValueFromEditor method

Public Overrides Function GetNewValueFromEditor(ByVal editor As Object) As Object
    Dim colorPicker As RadColorPicker = TryCast(editor, RadColorPicker)
    If colorPicker IsNot Nothing Then
        Return colorPicker.SelectedColor
    Else
        Return Nothing
    End If
End Function

As you can see first method gathers required information from the actual editor (used by the validation engine), after UI validation is successful then new value is submitted to the data item via second method. This second method returns errors (if any) that occurred while new value is set to the data item (Data layer validation).

In a scenario when there is a column.CellEditTemplate defined, the new value of the editor is not available in the arguments of the CellEditEnded event raised when commiting an edit. To get the right value in e.NewValue, you should override the column's GetNewValueFromEditor method.

See Also