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