Edit this page

Create Custom Column Editor

This tutorial will guide you through the common task of creating a custom column in RadGridView. More precisely, you will learn to create a UserControl with TextBox and RadComboBox, as well as a custom bound column that uses it as an edit element.

  • In the beginning you need RadGridView populated with sample data. Below are the business object definition (Example 1), as well as RadGridView declaration (Example 2).

Example 1: Business object definition

public class Club : INotifyPropertyChanged
{
    private string name;
    private DateTime established;
    private int stadiumCapacity;
    private Captain captain;

    public Club(string name, DateTime established, int stadiumCapacity, Captain captain)
    {
        this.name = name;
        this.established = established;
        this.stadiumCapacity = stadiumCapacity;
        this.captain = captain;
    }

    public String Name
    {
        get { return this.name; }
        set
        {
            if (this.name != value)
            {
                this.name = value;
                this.OnPropertyChanged("Name");
            }
        }
    }

    public DateTime Established
    {
        get
        {
            return this.established;
        }
        set
        {
            if (this.established != value)
            {
                this.established = value;
                this.OnPropertyChanged("Established");
            }
        }
    }

    public int StadiumCapacity
    {
        get { return this.stadiumCapacity; }
        set
        {
            if (this.stadiumCapacity != value)
            {
                this.stadiumCapacity = value;
                this.OnPropertyChanged("StadiumCapacity");
            }
        }
    }

    public Captain Captain
    {
        get
        {
            return this.captain;
        }
        set
        {
            if (this.captain != value)
            {
                this.captain = value;
                this.OnPropertyChanged("Captain");
            }
        }
    }

    public static ObservableCollection<Club> GetClubs()
    {
        ObservableCollection<Club> clubs = new ObservableCollection<Club>();

        clubs.Add(new Club("Liverpool", new DateTime(1892, 1, 1, 13, 35, 15), 45362, new Captain("Steven Gerrard", Position.MF)));
        clubs.Add(new Club("Manchester Utd.", new DateTime(1878, 1, 1, 18, 45, 25), 76212, new Captain("Wayne Rooney", Position.FW)));
        clubs.Add(new Club("Chelsea", new DateTime(1905, 1, 1, 23, 45, 35), 42055, new Captain("John Terry", Position.DF)));
        clubs.Add(new Club("Arsenal", new DateTime(1886, 1, 1, 4, 55, 45), 60355, new Captain("Mikel Arteta", Position.MF)));

        return clubs;
    }

    public event PropertyChangedEventHandler PropertyChanged;

    protected virtual void OnPropertyChanged(PropertyChangedEventArgs args)
    {
        PropertyChangedEventHandler handler = this.PropertyChanged;
        if (handler != null)
        {
            handler(this, args);
        }
    }

    private void OnPropertyChanged(string propertyName)
    {
        this.OnPropertyChanged(new PropertyChangedEventArgs(propertyName));
    }
}
Public Class Club
    Implements INotifyPropertyChanged
    Private _name As String
    Private _established As Date
    Private _stadiumCapacity As Integer
    Private _captain As Captain

    Public Sub New(ByVal name As String, ByVal established As Date, ByVal stadiumCapacity As Integer, ByVal captain As Captain)
        Me._name = name
        Me._established = established
        Me._stadiumCapacity = stadiumCapacity
        Me._captain = captain
    End Sub

    Public Property Name() As String
        Get
            Return Me._name
        End Get
        Set(ByVal value As String)
            If Me._name <> value Then
                Me._name = value
                Me.OnPropertyChanged("Name")
            End If
        End Set
    End Property

    Public Property Established() As Date
        Get
            Return Me._established
        End Get
        Set(ByVal value As Date)
            If Me._established <> value Then
                Me._established = value
                Me.OnPropertyChanged("Established")
            End If
        End Set
    End Property

    Public Property StadiumCapacity() As Integer
        Get
            Return Me._stadiumCapacity
        End Get
        Set(ByVal value As Integer)
            If Me._stadiumCapacity <> value Then
                Me._stadiumCapacity = value
                Me.OnPropertyChanged("StadiumCapacity")
            End If
        End Set
    End Property

    Public Property Captain() As Captain
        Get
            Return Me._captain
        End Get
        Set(ByVal value As Captain)
            If Me._captain IsNot value Then
                Me._captain = value
                Me.OnPropertyChanged("Captain")
            End If
        End Set
    End Property

    Public Shared Function GetClubs() As ObservableCollection(Of Club)
        Dim clubs As New ObservableCollection(Of Club)()

        clubs.Add(New Club("Liverpool", New Date(1892, 1, 1, 13, 35, 15), 45362, New Captain("Steven Gerrard", Position.MF)))
        clubs.Add(New Club("Manchester Utd.", New Date(1878, 1, 1, 18, 45, 25), 76212, New Captain("Wayne Rooney", Position.FW)))
        clubs.Add(New Club("Chelsea", New Date(1905, 1, 1, 23, 45, 35), 42055, New Captain("John Terry", Position.DF)))
        clubs.Add(New Club("Arsenal", New Date(1886, 1, 1, 4, 55, 45), 60355, New Captain("Mikel Arteta", Position.MF)))

        Return clubs
    End Function

    Public Event PropertyChanged As PropertyChangedEventHandler Implements INotifyPropertyChanged.PropertyChanged

    Protected Overridable Sub OnPropertyChanged(ByVal args As PropertyChangedEventArgs)
        Dim handler As PropertyChangedEventHandler = Me.PropertyChangedEvent
        If handler IsNot Nothing Then
            handler(Me, args)
        End If
    End Sub

    Private Sub OnPropertyChanged(ByVal propertyName As String)
        Me.OnPropertyChanged(New PropertyChangedEventArgs(propertyName))
    End Sub
End Class

Note, that the Club object has a Captain property. The Captain object itself, has two properties — Name, which is of type string, and Position, which is an Enum.

Example 2: Initial declaration of RadGridView

<telerik:RadGridView x:Name="radGridView" AutoGenerateColumns="False" ItemsSource="{Binding Clubs}">
    <telerik:RadGridView.Columns>
        <telerik:GridViewDataColumn DataMemberBinding="{Binding Name}" Header="Name" />
        <telerik:GridViewDataColumn DataFormatString="{}{0:N0}"
                        DataMemberBinding="{Binding StadiumCapacity}"
                        Header="Stadium" />
        <local:CustomColumn DataMemberBinding="{Binding Captain}" FilterMemberPath="Position"/>
    </telerik:RadGridView.Columns>
</telerik:RadGridView>

Example 3: Populating RadGridView

this.radGridView.ItemsSource = Club.GetClubs();
Me.radGridView.ItemsSource = Club.GetClubs()
  • The next step is to create a UserControl with TextBox and RadComboBox. Create a new UserControl named CustomCaptainEditor (Example 4 ).

Example 4: Declaration of CustomCaptainEditor UserControl

<Grid>
    <Grid.ColumnDefinitions>
        <ColumnDefinition />
        <ColumnDefinition />
    </Grid.ColumnDefinitions>
    <TextBox Text="{Binding CaptainName, Mode=TwoWay, RelativeSource={RelativeSource AncestorType=my:CustomCaptainEditor}}"/>
    <telerik:RadComboBox   Grid.Column="1" SelectedValue="{Binding CaptainPosition, Mode=TwoWay, RelativeSource={RelativeSource AncestorType=my:CustomCaptainEditor}}" 
                       ItemsSource="{Binding Positions, RelativeSource={RelativeSource AncestorType=my:CustomCaptainEditor}}"
                       DisplayMemberPath="Name" SelectedValuePath="Value"/>
</Grid>

Example 5: Code-behind definition of the CustomCaptainEditor UserControl

public partial class CustomCaptainEditor : UserControl
{
    public static readonly DependencyProperty CaptainNameProperty =
       DependencyProperty.Register("CaptainName", typeof(String), typeof(CustomCaptainEditor), new PropertyMetadata(null));

    public static readonly DependencyProperty CaptainPositionProperty =
        DependencyProperty.Register("CaptainPosition", typeof(Position), typeof(CustomCaptainEditor), new PropertyMetadata(null));

    public CustomCaptainEditor()
    {
        InitializeComponent();
    }

    public String CaptainName
    {
        get
        {
            return (String)this.GetValue(CaptainNameProperty);
        }
        set
        {
            this.SetValue(CaptainNameProperty, value);
        }
    }

    public Position CaptainPosition
    {
        get
        {
            return (Position)this.GetValue(CaptainPositionProperty);
        }
        set
        {
            this.SetValue(CaptainPositionProperty, value);
        }
    }

    private IEnumerable<EnumMemberViewModel> positions;

    public IEnumerable<EnumMemberViewModel> Positions
    {
        get
        {
            if (this.positions == null)
            {
                this.positions = EnumDataSource.FromType(typeof(Position));
            }
            return this.positions;
        }
    }
}
Partial Public Class CustomCaptainEditor
    Inherits UserControl

    Public Shared ReadOnly CaptainNameProperty As DependencyProperty = DependencyProperty.Register("CaptainName", GetType(String), GetType(CustomCaptainEditor), New PropertyMetadata(Nothing))

    Public Shared ReadOnly CaptainPositionProperty As DependencyProperty = DependencyProperty.Register("CaptainPosition", GetType(Position), GetType(CustomCaptainEditor), New PropertyMetadata(Nothing))

    Public Sub New()
        InitializeComponent()
    End Sub

    Public Property CaptainName() As String
        Get
            Return CStr(Me.GetValue(CaptainNameProperty))
        End Get
        Set(ByVal value As String)
            Me.SetValue(CaptainNameProperty, value)
        End Set
    End Property

    Public Property CaptainPosition() As Position
        Get
            Return CType(Me.GetValue(CaptainPositionProperty), Position)
        End Get
        Set(ByVal value As Position)
            Me.SetValue(CaptainPositionProperty, value)
        End Set
    End Property

    Private _positions As IEnumerable(Of EnumMemberViewModel)

    Public ReadOnly Property Positions() As IEnumerable(Of EnumMemberViewModel)
        Get
            If Me._positions Is Nothing Then
                Me._positions = EnumDataSource.FromType(GetType(Position))
            End If
            Return Me._positions
        End Get
    End Property
End Class

Take a look at the code-behind of the control. Two additional dependency properties are created in order to enable binding to the Name and Position properties of the business model.

  • Create a new class named CustomColumn, which derives from GridViewBoundColumnBase (Example 6).

Example 6: Definition of CustomColumn class

public class CustomColumn : GridViewBoundColumnBase
{
   public override FrameworkElement CreateCellElement(GridViewCell cell, object dataItem)
   {
       TextBlock tb = cell.Content as TextBlock;
       if (tb == null)
       {
           tb = new TextBlock();
           tb.SetBinding(TextBlock.TextProperty, new Binding(this.DataMemberBinding.Path.Path) { Converter = new MyConverter() });
       }

       return tb;
   }

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

       editor.SetBinding(CustomCaptainEditor.CaptainNameProperty,
           CreateBinding(string.Format("{0}.Name", this.DataMemberBinding.Path.Path)));
       editor.SetBinding(CustomCaptainEditor.CaptainPositionProperty,
           CreateBinding(string.Format("{0}.Position", this.DataMemberBinding.Path.Path)));

       return editor;
   }

   private Binding CreateBinding(string property)
   {
       Binding binding = new Binding(property);
       binding.Mode = BindingMode.TwoWay;
       binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged;

       return binding;
   }
}
Public Class CustomColumn
    Inherits GridViewBoundColumnBase

    Public Overrides Function CreateCellElement(ByVal cell As GridViewCell, ByVal dataItem As Object) As FrameworkElement
        Dim tb As TextBlock = TryCast(cell.Content, TextBlock)
        If tb Is Nothing Then
            tb = New TextBlock()
            tb.SetBinding(TextBlock.TextProperty, New Binding(Me.DataMemberBinding.Path.Path) With {.Converter = New MyConverter()})
        End If

        Return tb
    End Function

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

        editor.SetBinding(CustomCaptainEditor.CaptainNameProperty, CreateBinding(String.Format("{0}.Name", Me.DataMemberBinding.Path.Path)))
        editor.SetBinding(CustomCaptainEditor.CaptainPositionProperty, CreateBinding(String.Format("{0}.Position", Me.DataMemberBinding.Path.Path)))

        Return editor
    End Function

    Private Function CreateBinding(ByVal [property] As String) As Binding
        Dim binding As New Binding([property])
        binding.Mode = BindingMode.TwoWay
        binding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged

        Return binding
    End Function
End Class

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.

Here is the code of the custom converter we have used:

Example 7: The MyConverter class

public class MyConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var captain = value as Captain;
        if (String.IsNullOrEmpty(captain.Name))
        {
            return String.Format("{0}", captain.Position);
        }

        return String.Format("{0}, {1}", captain.Name, captain.Position);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value;
    }
}
public class MyConverter : IValueConverter
{
    public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        var captain = value as Captain;
        if (String.IsNullOrEmpty(captain.Name))
        {
            return String.Format("{0}", captain.Position);
        }

        return String.Format("{0}, {1}", captain.Name, captain.Position);
    }

    public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture)
    {
        return value;
    }
}
  • Run your demo and try to edit a cell from the new custom column. The result should be similar to the snapshot in Figure 1.

Figure 1: Snapshot of the created CustomColumn

The CustomColumn

You can download a runnable project of the previous example from the online SDK repository CustomColumnEditor.

You can also check the SDK Samples Browser that provides a more convenient approach to exploring and executing the examples in the Telerik XAML SDK repository.

See Also

Was this article helpful? Yes No

Give article feedback

Tell us how we can improve this article

Dummy