New to Telerik UI for WPF? Download free 30-day trial

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.

Example 1: Creating the RadColorPickerColumn class

public class RadColorPickerColumn : GridViewBoundColumnBase 
{ 
} 

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.

Example 2: Overriding the CreateCellElement method

public class RadColorPickerColumn : GridViewBoundColumnBase 
{ 
    public override FrameworkElement CreateCellElement(GridViewCell cell, object dataItem) 
    { 
        Border cellElement = cell.Content as Border; 
    if (cellElement == null)  
        {  
        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; 
    } 
} 

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.

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; 
    } 
} 
  • 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.

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; 
} 

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:

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; 
} 

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()

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)); 

Here is the full code for the RadColorPickerColumn class:

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)); 
} 
  • Use the just created custom column in XAML like the code below:

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

Telerik WPF DataGrid How To Create Custom Editor 010

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()

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(); 
} 
  • GetNewValueFromEditor()

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; 
    } 
} 

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.

In this article