This is a migrated thread and some comments may be shown as answers.

Custom Cell with GridDataCellElement and resize

7 Answers 36 Views
GridView
This is a migrated thread and some comments may be shown as answers.
David
Top achievements
Rank 2
Veteran
David asked on 02 Mar 2021, 05:17 AM

I've created a custom cell in a GridView with two labels.  It works so long as the grid isn't resized or the number of rows do not exceed the length of the grid.  When either of the two occur the custom cells can loose their formatting and cells without formatting adopt traits from the custom formatting.  

This snippet demonstrates the issue.

A bump in the right direction would be appreciated.

 

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;
using Telerik.WinControls.Layouts;
using Telerik.WinControls.UI;
 
namespace TR_Test
{
    public partial class GridTest : Telerik.WinControls.UI.RadForm
    {
        public GridTest()
        {
            InitializeComponent();
 
            //simple grid
            this.radGridView1.CreateCell += new Telerik.WinControls.UI.GridViewCreateCellEventHandler(this.radGridView1_CreateCell);
            this.radGridView1.AllowAddNewRow = false;
            this.radGridView1.AllowSearchRow = false;
            this.radGridView1.ShowGroupPanel = false;
            this.radGridView1.ShowRowHeaderColumn = false;
            this.radGridView1.TableElement.RowHeight = 60;
 
            // Populate the grid with data
            PopulateGrid();
 
            radGridView1.Columns["Info"].Width = 150;
        }
 
        private void PopulateGrid()
        {
            List<Sales> myList = new List<Sales>();
            myList.Add(new Sales(1, "Outdoor,1111", "asdf", "asdf"));
            myList.Add(new Sales(2, "Hardware,2222", "asdf", "asdf"));
            myList.Add(new Sales(3, "Tools,3333", "asdf", "asdf"));
            myList.Add(new Sales(4, "Books,4444", "asdf", "asdf"));
            myList.Add(new Sales(5, "Shows,5555", "asdf", "asdf"));
            myList.Add(new Sales(6, "Mugs,6666", "asdf", "asdf"));
            myList.Add(new Sales(7, "Phones,7777", "asdf", "asdf"));
            myList.Add(new Sales(8, "Indore,8888", "asdf", "asdf"));
            myList.Add(new Sales(9, "Cats,9999", "asdf", "asdf"));
            myList.Add(new Sales(10, "Dogs,0000", "asdf", "asdf"));
            myList.Add(new Sales(11, "Outdoor,1111", "asdf", "asdf"));
            myList.Add(new Sales(12, "Hardware,2222", "asdf", "asdf"));
            myList.Add(new Sales(13, "Tools,3333", "asdf", "asdf"));
            myList.Add(new Sales(14, "Books,4444", "asdf", "asdf"));
            myList.Add(new Sales(15, "Shows,5555", "asdf", "asdf"));
            myList.Add(new Sales(16, "Mugs,6666", "asdf", "asdf"));
            myList.Add(new Sales(17, "Phones,7777", "asdf", "asdf"));
            myList.Add(new Sales(18, "Indore,8888", "asdf", "asdf"));
            myList.Add(new Sales(19, "Cats,9999", "asdf", "asdf"));
            myList.Add(new Sales(20, "Dogs,0000", "asdf", "asdf"));
 
 
            radGridView1.BindingContext = new BindingContext();
            radGridView1.DataSource = myList;
        }
 
        private void radGridView1_CreateCell(object sender, Telerik.WinControls.UI.GridViewCreateCellEventArgs e)
        {
            if (e.CellType == typeof(GridDataCellElement))
            {
                GridViewDataColumn dataColumn = e.Column as GridViewDataColumn;
 
                switch (dataColumn.Name)
                {
                    case "Info":
                        e.CellType = typeof(SplitCell);
                        break;
                }
            }
        }
    }
 
    public class SplitCell : GridDataCellElement
    {
        private StackLayoutPanel panel;
        private RadLabelElement label1;
        private RadLabelElement label2;
 
        public SplitCell(GridViewColumn column, GridRowElement row) : base(column, row)
        { }
 
        protected override void CreateChildElements()
        {
            base.CreateChildElements();
 
            this.panel = new StackLayoutPanel
            {
                Margin = new System.Windows.Forms.Padding(5),
                Orientation = System.Windows.Forms.Orientation.Vertical
            };
 
            this.label1 = new RadLabelElement
            {
                Font = new Font("Segoe UI", 16.0f)
            };
            this.panel.Children.Add(this.label1);
 
            this.label2 = new RadLabelElement
            {
                Font = new Font("Segoe UI", 9.0f)
            };
            this.panel.Children.Add(this.label2);
 
            this.Children.Add(this.panel);
        }
 
        protected override void SetContentCore(object value)
        {
            object cellValue = value;
 
            this.label1.Text = "";
            this.label2.Text = "";
 
            if (cellValue is DBNull || cellValue == null)
                cellValue = ",";
 
            string[] s = cellValue.ToString().Split(',');
 
            if (s.Length >= 1)
                this.label1.Text = s[0];
 
            if (s.Length >= 2)
                this.label2.Text = s[1];
        }
    }
 
 
    public class Sales
    {
        public Sales(int id, string info, string PO, string Paid)
        {
            this.ID = id;
            this.Info = info;
            this.PO = PO;
            this.Paid = Paid;
        }
        public int ID { get; set; }
        public string Info { get; set; }
        public string PO { get; set; }
        public string Paid { get; set; }
    }
}

7 Answers, 1 is accepted

Sort by
0
Nadya
Telerik team
answered on 02 Mar 2021, 03:00 PM

Hello, David,

Note, RadGridView uses data virtualization which means that 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. "Info" column. 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. 

In addition, I noticed that you apply font for the labels in CreateChildElements() which may also cause an undesired look when cells are reused. The recommended way of applying any formatting styles in RadGridView is to use formatting events. More information you can find here: https://docs.telerik.com/devtools/winforms/controls/gridview/cells/formatting-cells

Please refer to the updated code snippet:

public partial class RadForm1 : Telerik.WinControls.UI.RadForm
{
    public RadForm1()
    {
        InitializeComponent();
        this.radGridView1.CreateCell += new Telerik.WinControls.UI.GridViewCreateCellEventHandler(this.radGridView1_CreateCell);
        this.radGridView1.AllowAddNewRow = false;
        this.radGridView1.AllowSearchRow = false;
        this.radGridView1.ShowGroupPanel = false;
        this.radGridView1.ShowRowHeaderColumn = false;
        this.radGridView1.TableElement.RowHeight = 60;

        // Populate the grid with data
        PopulateGrid();

        radGridView1.Columns["Info"].Width = 150;
    }
    private void PopulateGrid()
    {
        List<Sales> myList = new List<Sales>();
        myList.Add(new Sales(1, "Outdoor,1111", "asdf", "asdf"));
        myList.Add(new Sales(2, "Hardware,2222", "asdf", "asdf"));
        myList.Add(new Sales(3, "Tools,3333", "asdf", "asdf"));
        myList.Add(new Sales(4, "Books,4444", "asdf", "asdf"));
        myList.Add(new Sales(5, "Shows,5555", "asdf", "asdf"));
        myList.Add(new Sales(6, "Mugs,6666", "asdf", "asdf"));
        myList.Add(new Sales(7, "Phones,7777", "asdf", "asdf"));
        myList.Add(new Sales(8, "Indore,8888", "asdf", "asdf"));
        myList.Add(new Sales(9, "Cats,9999", "asdf", "asdf"));
        myList.Add(new Sales(10, "Dogs,0000", "asdf", "asdf"));
        myList.Add(new Sales(11, "Outdoor,1111", "asdf", "asdf"));
        myList.Add(new Sales(12, "Hardware,2222", "asdf", "asdf"));
        myList.Add(new Sales(13, "Tools,3333", "asdf", "asdf"));
        myList.Add(new Sales(14, "Books,4444", "asdf", "asdf"));
        myList.Add(new Sales(15, "Shows,5555", "asdf", "asdf"));
        myList.Add(new Sales(16, "Mugs,6666", "asdf", "asdf"));
        myList.Add(new Sales(17, "Phones,7777", "asdf", "asdf"));
        myList.Add(new Sales(18, "Indore,8888", "asdf", "asdf"));
        myList.Add(new Sales(19, "Cats,9999", "asdf", "asdf"));
        myList.Add(new Sales(20, "Dogs,0000", "asdf", "asdf"));


        radGridView1.BindingContext = new BindingContext();
        radGridView1.DataSource = myList;
       this.radGridView1.CellFormatting += this.RadGridView1_CellFormatting;
    }
    Font font1 = new Font("Segoe UI", 16.0f);
    Font font2 = new Font("Segoe UI", 9.0f);
    private void RadGridView1_CellFormatting(object sender, CellFormattingEventArgs e)
    {
        var cell = e.CellElement as SplitCell;
        if (cell != null)
        {
            cell.label1.Font = font1;
            cell.label2.Font = font2;
        }
    }

    private void radGridView1_CreateCell(object sender, Telerik.WinControls.UI.GridViewCreateCellEventArgs e)
    {
        if (e.CellType == typeof(GridDataCellElement))
        {
            GridViewDataColumn dataColumn = e.Column as GridViewDataColumn;

            switch (dataColumn.Name)
            {
                case "Info":
                    e.CellType = typeof(SplitCell);
                    break;
            }
        }
    }
}

public class SplitCell : GridDataCellElement
{
    private StackLayoutPanel panel;
    public RadLabelElement label1;
    public RadLabelElement label2;

    public SplitCell(GridViewColumn column, GridRowElement row) : base(column, row)
    { }

    protected override void CreateChildElements()
    {
        base.CreateChildElements();

        this.panel = new StackLayoutPanel
        {
            Margin = new System.Windows.Forms.Padding(5),
            Orientation = System.Windows.Forms.Orientation.Vertical
        };

        this.label1 = new RadLabelElement
        {
          // Font = new Font("Segoe UI", 16.0f)
        };
        this.panel.Children.Add(this.label1);

        this.label2 = new RadLabelElement
        {
        //  Font = new Font("Segoe UI", 9.0f)
        };
        this.panel.Children.Add(this.label2);

        this.Children.Add(this.panel);
    }

    protected override void SetContentCore(object value)
    {
        object cellValue = value;

        this.label1.Text = "";
        this.label2.Text = "";

        if (cellValue is DBNull || cellValue == null)
            cellValue = ",";

        string[] s = cellValue.ToString().Split(',');

        if (s.Length >= 1)
            this.label1.Text = s[0];

        if (s.Length >= 2)
            this.label2.Text = s[1];
    }
    public override bool IsCompatible(GridViewColumn data, object context)
    {
        return data.Name == "Info" && context is GridDataRowElement;
    }
}


public class Sales
{
    public Sales(int id, string info, string PO, string Paid)
    {
        this.ID = id;
        this.Info = info;
        this.PO = PO;
        this.Paid = Paid;
    }
    public int ID { get; set; }
    public string Info { get; set; }
    public string PO { get; set; }
    public string Paid { get; set; }
}

I hope this helps. Let me know if you have other questions.

Regards,
Nadya
Progress Telerik

Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.

0
David
Top achievements
Rank 2
Veteran
answered on 02 Mar 2021, 07:24 PM

Hi Nadya,

 

Thanks for the insight.   The custom cells perform much better - when collapsing the grid either horizontally or vertically, they work great. But the cells continue to loose their custom formatting when the grid is resized to zero.  I've implemented a minimum grid size to prevent a zero size from occurring. 

 

Attached is a gif of the behavior using the lower right corner grip.  

0
Nadya
Telerik team
answered on 03 Mar 2021, 10:48 AM

Hello, David,

If you are still having issues with the applied formatting it is due to the UI virtualization. In order to prevent applying the formatting to other cell elements (because of the cell reuse) all customization should be reset for the rest of the cell elements. This can be done by providing an 'else'-clause where to reset all the modified properties. 

In your case, you have a custom cell with two labels and you want to have a different font for the labels. By overriding IsCompatible method, you specify that this cell is compatible only with the specified column. However, when you resize the grid to a minimum its cells become hidden, then after maximizing the grid they become visible again. This may cause issues with different formatting for the labels in the same cell. To overcome this, I could suggest using one label in the cell instead of two. For example, you can use the CellElement.Text to display the first string and one label for the second string. Thus, you should be able to reset the font properly.

I modified the CreateChildElements() in order to demonstrate this approach:

protected override void CreateChildElements()
{
    base.CreateChildElements();

    this.panel = new StackLayoutPanel
    {
        Margin = new System.Windows.Forms.Padding(5),
        Orientation = System.Windows.Forms.Orientation.Vertical
    };

    this.label1 = new RadLabelElement
    {
        Font = new Font("Segoe UI", 16.0f)
    };
    //this.panel.Children.Add(this.label1);

    this.label2 = new RadLabelElement
    {
        Font = new Font("Segoe UI", 9.0f)
    };
    this.panel.Children.Add(this.label2);

    this.Children.Add(this.panel);
}

protected override void SetContentCore(object value)
{
    object cellValue = value;

    this.label1.Text = "";
    this.label2.Text = "";

    if (cellValue is DBNull || cellValue == null)
        cellValue = ",";

    string[] s = cellValue.ToString().Split(',');

    if (s.Length >= 1)
        this.Text = s[0];

    if (s.Length >= 2)
        this.label2.Text = s[1];
}

In the CellFormatting event you should  reset the Font property:

private void RadGridView1_CellFormatting(object sender, CellFormattingEventArgs e)
{
    var cell = e.CellElement as SplitCell;
    if (cell != null)
    {
        if (cell.ColumnInfo.Name == "Info")
        {
            cell.Font = font1;
            cell.label2.Font = font2;
        }
        else
        {
            cell.ResetValue(LightVisualElement.FontProperty, ValueResetFlags.Local);
            cell.label2.ResetValue(LightVisualElement.FontProperty, ValueResetFlags.Local);
        }
    }
    else
    {
        e.CellElement.ResetValue(LightVisualElement.FontProperty, ValueResetFlags.Local);
    }
}

I hope this helps. Let me know if you have other questions.

Regards,
Nadya
Progress Telerik

Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.

0
David
Top achievements
Rank 2
Veteran
answered on 03 Mar 2021, 04:01 PM

Hi Nadya,

I feel as thought there is too much going on and the layout is confusing the issue.

This is not a CellFormatting issue.  This is the cells in the second column, (Column[1]), outright is loosing it's custom control when the grid is resized to zero.

I've simplified the code.  Stripped it of all layouts and label formatting aside from RowHeight and Column[1].Width.  

There are three columns and two rows. 

  • The first column always displays a 1 or 2, which is right.
  • The second column should display correct, which breaks on resize and displays LOST MY OBJECT
  • The third column is always blank, which is right  

Grid Issue GIF

public partial class RadForm1 : Telerik.WinControls.UI.RadForm
 {
     public RadForm1()
     {
         InitializeComponent();
 
         radGridView1.TableElement.RowHeight = 80;
         radGridView1.BindingContext = new BindingContext();
         radGridView1.DataSource = new List<Sales>
         {
             new Sales(1),
             new Sales(2)
         };
 
         radGridView1.Columns[1].Width = 150;
     }
 
     private void radGridView1_CreateCell(object sender, GridViewCreateCellEventArgs e)
     {
         if (e.CellType == typeof(GridDataCellElement) && e.Column.Index == 1)
             e.CellType = typeof(SplitCell);
     }
 }
 
 public class SplitCell : GridDataCellElement
 {
     public RadLabelElement label1 = new RadLabelElement();
 
     public SplitCell(GridViewColumn column, GridRowElement row) : base(column, row)
     { }
 
     protected override void CreateChildElements()
     {
         base.CreateChildElements();
 
         this.Children.Add(this.label1);
     }
 
     protected override void SetContentCore(object value)
     {
         this.label1.Text = "correct";
     }
 
     public override bool IsCompatible(GridViewColumn data, object context)
     {
         return data.Index == 1 && context is GridDataRowElement;
     }
 }
 
 public class Sales
 {
     public Sales(int id)
     {
         this.PO = "";
         this.ID = id;
         this.Info = "LOST MY OBJECT";
     }
     public int ID { get; set; }
     public string Info { get; set; }
     public string PO { get; set; }
 }

 

 

 

 

 

 

0
Nadya
Telerik team
answered on 04 Mar 2021, 10:18 AM

Hello, David,

The above-described issue with displaying the wrong text in the second column is again related to the UI virtualization that RadGridView uses for its cells/rows. I would like to note that sometimes some of the existing GridDataCellElements may be reused for the columns in certain situations. You can handle this situation when you specify that your SplitCell is compatible with only the first column and the rest of the columns don't use this particular cell. For this purpose, it would be necessary to create another derivative of the GridDataCellElement class and override its IsCompatible method as well in order to specify that it is applicable for all the rest columns except the one with Column.Index == 1. 

I updated the provided code snippet accordingly:

public partial class RadForm2 : Telerik.WinControls.UI.RadForm
{
    public RadForm2()
    {
        InitializeComponent();
        this.radGridView1.CreateCell += this.RadGridView1_CreateCell;
        radGridView1.TableElement.RowHeight = 80;
        radGridView1.BindingContext = new BindingContext();
        radGridView1.DataSource = new List<Sales>
     {
         new Sales(1),
         new Sales(2)
     };

        radGridView1.Columns[1].Width = 150;
    }

    private void RadGridView1_CreateCell(object sender, Telerik.WinControls.UI.GridViewCreateCellEventArgs e)
    {
        if (e.CellType == typeof(GridDataCellElement))
            {
            if (e.Column.Index == 1)
                e.CellType = typeof(SplitCell);
            else
            {
                e.CellType = typeof(CustomCell);
            }
        }
    }
}
public class SplitCell : GridDataCellElement
{
    public RadLabelElement label1 = new RadLabelElement();

    public SplitCell(GridViewColumn column, GridRowElement row) : base(column, row)
    { }

    protected override void CreateChildElements()
    {
        base.CreateChildElements();

        this.Children.Add(this.label1);
    }

    protected override void SetContentCore(object value)
    {
        this.label1.Text = "correct";
    }

    public override bool IsCompatible(GridViewColumn data, object context)
    {
        return data.Index == 1 && context is GridDataRowElement;
    }
}

public class CustomCell : GridDataCellElement
{
    public RadLabelElement label1 = new RadLabelElement();

    public CustomCell(GridViewColumn column, GridRowElement row) : base(column, row)
    { }

    public override bool IsCompatible(GridViewColumn data, object context)
    {
        return data.Index != 1 && context is GridDataRowElement;
    }
}

public class Sales
{
    public Sales(int id)
    {
        this.PO = "";
        this.ID = id;
        this.Info = "LOST MY OBJECT";
    }
    public int ID { get; set; }
    public string Info { get; set; }
    public string PO { get; set; }
}


More information and example regarding cell reuse you can find here: 
https://docs.telerik.com/devtools/winforms/knowledge-base/custom-gridview-cells-with-input-elements 
https://docs.telerik.com/devtools/winforms/controls/gridview/fundamentals/ui-virtualization#ui-virtualization 

I hope this helps.

Regards,
Nadya
Progress Telerik

Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.

0
David
Top achievements
Rank 2
Veteran
answered on 04 Mar 2021, 03:00 PM

 

Hi Nadya,

I see, said the blind man as he picked up his hammer and saw.

And there it was: https://docs.telerik.com/devtools/winforms/knowledge-base/custom-gridview-cells-with-input-elements  I missed that page with pretty much the same solution.

So a best practice is to use a default object, and formatting, for each cell when using using custom cells.

 

Thanks for your assistance!

0
Accepted
Nadya
Telerik team
answered on 04 Mar 2021, 03:09 PM

Hello, David,

I am really glad that the provided information and referred article were useful. If you have any other questions do not hesitate to contact us. 

Regards,
Nadya
Progress Telerik

Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.

Tags
GridView
Asked by
David
Top achievements
Rank 2
Veteran
Answers by
Nadya
Telerik team
David
Top achievements
Rank 2
Veteran
Share this question
or