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

How to Create Custom Cells with Input Elements

Environment

Product Version Product Author
2020.1.218 RadGridView for WinForms Desislava Yordanova

Description

RadGridView provides a convenient approach for creating custom cell elements. A common requirement is host input controls in the grid cells that provide build-in editing without entering edit mode.

Please have in mind that using controls in grid cells may slow down the scrolling and will cause visual glitches as they do not support clipping. A better option would be using custom editors. Thus, you can construct the desired editor and activate it for the respective cell.

This article demonstrates how to use RadElements instead of RadControls in the grid cells in order to achieve the following result:

custom-gridview-cells-with-input-elements 001

Solution

Before setting the DataSource of RadGridView, it is necessary to subscribe to the CreateCell event. In the GridViewCreateCellEventArgs you can replace the CellElement considering the CellType. The code snippet below shows how to replace the default cell element for the ContactName column. The custom defined cell hosts a RadTextBoxControlElement that updates the ContactName field of the DataBoundItem and a RadDropDownListElement that manages the ContactTitle.

Since RadGridView uses data virtualization, cell elements are created only for currently visible cells and they are being reused during operations like scrolling, filtering, etc. It is necessary to specify that your custom cell is applicable only to the desired column, e.g. ContactName. For this purpose, it is necessary to override the IsCompatible method and return true only if the cell is relevant for this column and row. This will ensure that the custom cell will be used only in this particular column and it won't be reused in other columns. However, the cell elements belonging to the rest of the columns, by default, are applicable to your column (ContactName) as well. This requires creating a default GridDataCellElement which IsCompatible with all the columns but ContactName.

Please refer to the complete code snippet:


private void RadForm1_Load(object sender, EventArgs e)
{
    this.radGridView1.CreateCell += radGridView1_CreateCell;

    this.radGridView1.DataSource = this.customersBindingSource;
    this.customersTableAdapter.Fill(this.nwindDataSet.Customers);
    this.radGridView1.BestFitColumns();

    this.radGridView1.CellBeginEdit+=radGridView1_CellBeginEdit;
}

private void radGridView1_CellBeginEdit(object sender, GridViewCellCancelEventArgs e)
        {
            if (e.Column.Name=="ContactName")
            {
                e.Cancel = true;
            }
        }

private void radGridView1_CreateCell(object sender, GridViewCreateCellEventArgs e)
        {
            if (e.CellType == typeof(GridDataCellElement))
            {
                if (e.Column.Name == "ContactName")
                {
                    e.CellElement = new CustomGridDataCellElement(e.Column, e.Row);
                }
                else
                {
                    e.CellElement = new DefaultGridDataCellElement(e.Column, e.Row);
                }
            }
        }

public class DefaultGridDataCellElement : GridDataCellElement
{
    public DefaultGridDataCellElement(GridViewColumn column, GridRowElement row)
        : base(column, row)
            {
            }

    protected override Type ThemeEffectiveType
            {
                get
                {
                    return typeof(GridDataCellElement);
                }
            }

    public override bool IsCompatible(GridViewColumn data, object context)
            {
                //use this cell for all columns but ContactName
                if (data.Name != "ContactName")
                {
                    return true;
                }
                return false;
            }
}

public class CustomGridDataCellElement : GridDataCellElement
{
    public CustomGridDataCellElement(GridViewColumn column, GridRowElement row)
        : base(column, row)
            {
            }

    protected override Type ThemeEffectiveType
            {
                get
                {
                    return typeof(GridDataCellElement);
                }
            }

    public override bool IsCompatible(GridViewColumn data, object context)
            {
                //use this cell only for ContactName column
                if (data.Name == "ContactName")
                {
                    return true;
                }
                return false;
            }


    StackLayoutElement mainContainer;
    RadTextBoxControlElement nameElement;
    RadDropDownListElement contactTitleDropDown;

    protected override void CreateChildElements()
            {
                base.CreateChildElements();
                mainContainer = new StackLayoutElement();
                mainContainer.StretchHorizontally = true;
                mainContainer.Orientation = Orientation.Horizontal;

                contactTitleDropDown = new RadDropDownListElement();
                contactTitleDropDown.DropDownStyle = RadDropDownStyle.DropDownList;
                contactTitleDropDown.Items.Add("Accounting Manager");
                contactTitleDropDown.Items.Add("Assistant Sales Representative");
                contactTitleDropDown.Items.Add("Marketing Assistant");
                contactTitleDropDown.Items.Add("Marketing Manager");
                contactTitleDropDown.Items.Add("Order Administrator");
                contactTitleDropDown.Items.Add("Owner");
                contactTitleDropDown.Items.Add("Owner/Marketing Assistant");
                contactTitleDropDown.Items.Add("Sales Agent");
                contactTitleDropDown.Items.Add("Sales Associate");
                contactTitleDropDown.Items.Add("Sales Manager");
                contactTitleDropDown.Items.Add("Sales Representative");
                contactTitleDropDown.SelectedIndexChanged += contactTitleDropDown_SelectedIndexChanged;

                nameElement = new RadTextBoxControlElement();
                nameElement.StretchHorizontally = false;
                nameElement.TextChanged += nameElement_TextChanged;

                mainContainer.Children.Add(nameElement);
                mainContainer.Children.Add(contactTitleDropDown);
                this.Children.Add(mainContainer);
            }

    private void nameElement_TextChanged(object sender, EventArgs e)
            {
                DataRowView rowView = this.RowInfo.DataBoundItem as DataRowView;
                rowView.Row["ContactName"] = nameElement.Text;
            }

    private void contactTitleDropDown_SelectedIndexChanged(object sender, Telerik.WinControls.UI.Data.PositionChangedEventArgs e)
            {
                DataRowView rowView = this.RowInfo.DataBoundItem as DataRowView;
                if (e.Position > -1)
                {
                    rowView.Row["ContactTitle"] = contactTitleDropDown.Items[e.Position].Text;
                }
            }

    protected override void SetContentCore(object value)
            {
                base.SetContentCore(value);

                if (this.RowInfo != null)
                {
                    DataRowView rowView = this.RowInfo.DataBoundItem as DataRowView;
                    if (rowView != null)
                    {
                        nameElement.TextChanged -= nameElement_TextChanged;
                        nameElement.Text = rowView.Row["ContactName"].ToString();
                        nameElement.TextChanged += nameElement_TextChanged;
                        contactTitleDropDown.SelectedIndexChanged -= contactTitleDropDown_SelectedIndexChanged;
                        contactTitleDropDown.SelectedIndex = GetIndexByText(rowView["ContactTitle"].ToString());
                        contactTitleDropDown.SelectedIndexChanged += contactTitleDropDown_SelectedIndexChanged;
                    }
                }
            }

    private int GetIndexByText(string contactName)
            {
                int index = -1;
                for (int i = 0; i < contactTitleDropDown.Items.Count; i++)
                {
                    if (contactTitleDropDown.Items[i].Text == contactName)
                    {
                        index = i;
                        break;
                    }
                }
                return index;
            }
}



Private Sub RadForm1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    AddHandler Me.RadGridView1.CreateCell, AddressOf radGridView1_CreateCell
    Me.RadGridView1.DataSource = Me.CustomersBindingSource
    Me.CustomersTableAdapter.Fill(Me.NwindDataSet.Customers)
    Me.RadGridView1.BestFitColumns()
    AddHandler Me.RadGridView1.CellBeginEdit, AddressOf radGridView1_CellBeginEdit
End Sub

Private Sub radGridView1_CellBeginEdit(ByVal sender As Object, ByVal e As GridViewCellCancelEventArgs)
    If e.Column.Name = "ContactName" Then
        e.Cancel = True
    End If
End Sub

Private Sub radGridView1_CreateCell(ByVal sender As Object, ByVal e As GridViewCreateCellEventArgs)
    If e.CellType = GetType(GridDataCellElement) Then

        If e.Column.Name = "ContactName" Then
            e.CellElement = New CustomGridDataCellElement(e.Column, e.Row)
        Else
            e.CellElement = New DefaultGridDataCellElement(e.Column, e.Row)
        End If
    End If
End Sub

Public Class DefaultGridDataCellElement
    Inherits GridDataCellElement

    Public Sub New(ByVal column As GridViewColumn, ByVal row As GridRowElement)
        MyBase.New(column, row)
    End Sub

    Protected Overrides ReadOnly Property ThemeEffectiveType As Type
        Get
            Return GetType(GridDataCellElement)
        End Get
    End Property

    Public Overrides Function IsCompatible(ByVal data As GridViewColumn, ByVal context As Object) As Boolean
        If data.Name <> "ContactName" Then
            Return True
        End If

        Return False
    End Function
End Class

Public Class CustomGridDataCellElement
    Inherits GridDataCellElement

    Public Sub New(ByVal column As GridViewColumn, ByVal row As GridRowElement)
        MyBase.New(column, row)
    End Sub

    Protected Overrides ReadOnly Property ThemeEffectiveType As Type
        Get
            Return GetType(GridDataCellElement)
        End Get
    End Property

    Public Overrides Function IsCompatible(ByVal data As GridViewColumn, ByVal context As Object) As Boolean
        If data.Name = "ContactName" Then
            Return True
        End If

        Return False
    End Function

    Private mainContainer As StackLayoutElement
    Private nameElement As RadTextBoxControlElement
    Private contactTitleDropDown As RadDropDownListElement

    Protected Overrides Sub CreateChildElements()
        MyBase.CreateChildElements()
        mainContainer = New StackLayoutElement()
        mainContainer.StretchHorizontally = True
        mainContainer.Orientation = Orientation.Horizontal
        contactTitleDropDown = New RadDropDownListElement()
        contactTitleDropDown.DropDownStyle = RadDropDownStyle.DropDownList
        contactTitleDropDown.Items.Add("Accounting Manager")
        contactTitleDropDown.Items.Add("Assistant Sales Representative")
        contactTitleDropDown.Items.Add("Marketing Assistant")
        contactTitleDropDown.Items.Add("Marketing Manager")
        contactTitleDropDown.Items.Add("Order Administrator")
        contactTitleDropDown.Items.Add("Owner")
        contactTitleDropDown.Items.Add("Owner/Marketing Assistant")
        contactTitleDropDown.Items.Add("Sales Agent")
        contactTitleDropDown.Items.Add("Sales Associate")
        contactTitleDropDown.Items.Add("Sales Manager")
        contactTitleDropDown.Items.Add("Sales Representative")
        AddHandler contactTitleDropDown.SelectedIndexChanged, AddressOf contactTitleDropDown_SelectedIndexChanged
        nameElement = New RadTextBoxControlElement()
        nameElement.StretchHorizontally = False
        AddHandler nameElement.TextChanged, AddressOf nameElement_TextChanged
        mainContainer.Children.Add(nameElement)
        mainContainer.Children.Add(contactTitleDropDown)
        Me.Children.Add(mainContainer)
    End Sub

    Private Sub nameElement_TextChanged(ByVal sender As Object, ByVal e As EventArgs)
        Dim rowView As DataRowView = TryCast(Me.RowInfo.DataBoundItem, DataRowView)
        rowView.Row("ContactName") = nameElement.Text
    End Sub

    Private Sub contactTitleDropDown_SelectedIndexChanged(ByVal sender As Object, ByVal e As Telerik.WinControls.UI.Data.PositionChangedEventArgs)
        Dim rowView As DataRowView = TryCast(Me.RowInfo.DataBoundItem, DataRowView)

        If e.Position > -1 Then
            rowView.Row("ContactTitle") = contactTitleDropDown.Items(e.Position).Text
        End If
    End Sub

    Protected Overrides Sub SetContentCore(ByVal value As Object)
        MyBase.SetContentCore(value)

        If Me.RowInfo IsNot Nothing Then
            Dim rowView As DataRowView = TryCast(Me.RowInfo.DataBoundItem, DataRowView)

            If rowView IsNot Nothing Then
                RemoveHandler nameElement.TextChanged, AddressOf nameElement_TextChanged
                nameElement.Text = rowView.Row("ContactName").ToString()
                AddHandler nameElement.TextChanged, AddressOf nameElement_TextChanged
                RemoveHandler contactTitleDropDown.SelectedIndexChanged, AddressOf contactTitleDropDown_SelectedIndexChanged
                contactTitleDropDown.SelectedIndex = GetIndexByText(rowView("ContactTitle").ToString())
                AddHandler contactTitleDropDown.SelectedIndexChanged, AddressOf contactTitleDropDown_SelectedIndexChanged
            End If
        End If
    End Sub

    Private Function GetIndexByText(ByVal contactName As String) As Integer
        Dim index As Integer = -1

        For i As Integer = 0 To contactTitleDropDown.Items.Count - 1

            If contactTitleDropDown.Items(i).Text = contactName Then
                index = i
                Exit For
            End If
        Next

        Return index
    End Function
End Class

See Also

In this article