GridView cell formatting in custom columns

1 Answer 25 Views
GridView
Marian
Top achievements
Rank 2
Bronze
Iron
Iron
Marian asked on 30 Dec 2025, 05:01 PM

Hello,

I would like to ask two questions about custom columns in GridView. I'm putting them into one thread since they're related.

1. What's the relationship between SetContentCore and OnCellFormatting? Is it ok to set formatting in SetContentCore? This is how it seems to me to be correct:

		protected override void SetContentCore(object value)
		{
			if (value is Quality q)
			{
				if (q == Quality.None)
					Text = "--";
				else
					Text = q.ToString();
			}
			else
				base.SetContentCore(value);
		}

		protected override void OnCellFormatting(CellFormattingEventArgs e)
		{
			ResetValue(VisualElement.BackColorProperty, ValueResetFlags.Local);
			ResetValue(VisualElement.ForeColorProperty, ValueResetFlags.Local);
			ResetValue(LightVisualElement.NumberOfColorsProperty, ValueResetFlags.Local);
			ResetValue(LightVisualElement.DrawFillProperty, ValueResetFlags.Local);

			if (Value is Quality q)
			{
				switch (q)
				{
					case Quality.OK:
						BackColor = Color.Lime;
						NumberOfColors = 1;
						DrawFill = true;
						break;
					case Quality.NOK:
						BackColor = Color.Red;
						NumberOfColors = 1;
						DrawFill = true;
						break;
					case Quality.Error:
						BackColor = Color.DodgerBlue;
						NumberOfColors = 1;
						DrawFill = true;
						break;
				}
			}

			base.OnCellFormatting(e);
		}

But this also works. I tried it mainly because of the second question, and Copilot actually suggested it as the first styling approach.

		protected override void SetContentCore(object value)
		{
			if (value is Quality q)
			{
				if (q == Quality.None)
					Text = "--";
				else
					Text = q.ToString();

				ResetValue(VisualElement.BackColorProperty, ValueResetFlags.Local);
				ResetValue(VisualElement.ForeColorProperty, ValueResetFlags.Local);
				ResetValue(LightVisualElement.NumberOfColorsProperty, ValueResetFlags.Local);
				ResetValue(LightVisualElement.DrawFillProperty, ValueResetFlags.Local);

				switch (q)
				{
					case Quality.OK:
						BackColor = Color.Lime;
						NumberOfColors = 1;
						DrawFill = true;
						break;
					case Quality.NOK:
						BackColor = Color.Red;
						NumberOfColors = 1;
						DrawFill = true;
						break;
					case Quality.Error:
						BackColor = Color.DodgerBlue;
						NumberOfColors = 1;
						DrawFill = true;
						break;
				}
			}
			else
				base.SetContentCore(value);
		}

 

2. Is there any way to override how Value is retrieved from the DataBoundItem? In one of my column types, I can't use FieldName directly. In classic DataGridView, I could override GetValue / SetValue like this:

		protected override object GetValue(int rowIndex)
		{
			DgvRealResultColumn col = OwningColumn as DgvRealResultColumn;
			object obj = DataGridView.Rows[rowIndex].DataBoundItem;
			if ((col == null) || (obj == null))
				return base.GetValue(rowIndex);

			object resultValue = GetResultValue(rowIndex, col, obj);
			Quality q = (Quality)Convert.ToInt32(resultValue);
			if (!QualityHelper.HasValue(q))
				return null;

			if (obj is RealResult rr)
				return rr.Value;

			string propName = string.Format("R{0:D3}Value", col.ItemIndex);
			PropertyInfo pi = obj.GetType().GetProperty(propName);
			return pi.GetValue(obj, null);
		}

I don't see anything like that in Telerik GridView. So far, I came up with this workaround:

		protected override void SetContentCore(object value)
		{
			TestResult r = null;
			var obj = RowInfo.DataBoundItem;
			if (obj is TestResult)
				r = (TestResult)obj;
			else if (obj is TestItem item)
			{
				var col = ColumnInfo as RgvTestResultColumn;
				if ((col != null) && (item.Results != null) && (col.ResultIndex < item.Results.Length))
					r = item.Results[col.ResultIndex];
			}

			ResetValue(VisualElement.BackColorProperty, ValueResetFlags.Local);
			ResetValue(VisualElement.ForeColorProperty, ValueResetFlags.Local);
			ResetValue(LightVisualElement.NumberOfColorsProperty, ValueResetFlags.Local);
			ResetValue(LightVisualElement.DrawFillProperty, ValueResetFlags.Local);

			if (r == null)
			{
				base.SetContentCore(value);
				return;
			}

			switch (r.Result)
			{
				case Quality.OK:
					Text = r.Value.ToString("F1");
					BackColor = Color.Lime;
					NumberOfColors = 1;
					DrawFill = true;
					break;
				case Quality.NOK:
					Text = r.Value.ToString("F1");
					BackColor = Color.Red;
					NumberOfColors = 1;
					DrawFill = true;
					break;
				case Quality.Error:
					Text = "X";
					BackColor = Color.DodgerBlue;
					NumberOfColors = 1;
					DrawFill = true;
					break;
				default:
					Text = "--";
					break;
			}
		}

But in this case, it doesn't set the internal Value, so when I write rgv.Rows[0].Cells["colR0"].Value in code, I always get null. Is there any way to solve this? I am attaching my test code for testing.

1 Answer, 1 is accepted

Sort by
0
Dinko | Tech Support Engineer
Telerik team
answered on 01 Jan 2026, 12:55 PM

Hi Marian,

Thank you for reaching out to us and for the provided project.

Let me start with your first question:

SetContentCore is intended for updating the cell's displayed content (such as Text, images, etc.) based on the cell value. It is called when the cell value changes or needs to be rendered. On the other hand, OnCellFormatting is specifically designed for applying visual styles (such as BackColor, ForeColor, etc.) during the cell formatting phase. The cell formatting is called on different occasions, such as mouse interactions, scrolling, form (parent) resizing, DPI change, etc.  

The recommended approach here will be to change the content of the cell (Text property) in SetContentCore() or change the custom element displayed value if such is added. At the same time, any other visual styling needs to be placed in the OnCellFormatting (CellFormatting event of the RadGridView). The cell formatting events are called quite often. The SetContentCore() may not be called in some scenarios, and you could observe some delay in applying the custom styling.

For the second question, keep in mind that there is a difference between the Text and the Value property of a cell. The Text property is used for the visual appearance of the underlying Value property. In your case, the RgvTestResultColumn custom column does not have a FieldName property set. Thus, the Value property will always be null. The visual text is structured entirely in code. The tricky part is that the Text property is part of the visual cell. Due to virtualization, the cells are reused. So you can't get the Text of a cell that is out of the view. What I can suggest as an alternative is to use the DataBoundItem property of the row again. 

rgv.Rows[0].DataBoundItem as TestItem

Another approach that you could consider is to expose a property in the custom object that makes all the modifications and binds a default GridViewTextBoxColumn to it. Then you can only need to apply the custom visual settings in the CellFormatting event of the RadGridView. This way, you won't need to create a custom cell.

Let me know if the above approach will work for you.

Regards,
Dinko | Tech Support Engineer
Progress Telerik

Love the Telerik and Kendo UI products and believe more people should try them? Invite a fellow developer to become a Progress customer and each of you can get a $50 Amazon gift voucher.

Marian
Top achievements
Rank 2
Bronze
Iron
Iron
commented on 01 Jan 2026, 08:46 PM

Hello,
Regarding the second question. I'm aware of the difference between the Text and Value properties, therefore I asked whether it's possible to override how Value is retrieved from the DataBoundItem, similar to how I can override GetValue / SetValue in classic DataGridView. When I tried custom value retrieval inside SetContentCore, it worked visually as expected. But of course, Value remained null, since the standard mechanism didn't set it, because FieldName was empty.

In my case, getting value should work like this:
- if the current Value is already a TestResult (if FieldName was set to a property of that type), use it
- if the current Value is null (because FieldName was empty) and the DataBoundItem is a TestItem, then retrieve the TestResult from TestItem.Results using the ResultIndex defined in the column.

Alternatively, can I set FieldName to something like "Results[2]"? Would that work? Yes, I could create properties like Result0, Result1, etc., but if I have 200 results, I think it's not a correct way. In classic WinForms DataGridView, I can override GetValue and modify process of getting value from the bound object as I want. In Telerik GridView, the only way I have found so far is to do it in SetContentCore (as shown above), but that only affects the visual part, the internal Value remains empty of course, and I assume that means sorting and filtering won't work either.

Dinko | Tech Support Engineer
Telerik team
commented on 02 Jan 2026, 10:46 AM

I understand your point here. Indeed, the WinForms DataGridView exposes a way to override the GetValue method. However, this is currently not possible in our RadGridView control. The SetContentCore() method is part of the visual GridDataCellElement. These cells are used by the virtualization mechanism, and the object underneath is GridViewCellInfo. The Value property in the following syntax is part of the GridViewCellInfo, not the Value of the GridDataCellElement.

rgv.Rows[0].Cells["colR0"].Value

The current implementation of the RadGridView control does not allow you to replace the GridViewCellInfo object with your own implementation. 

I see that creating 200 properties is not an option. Upon checking the source code again, I came up with a possible solution, which you could check. From reading your custom code, the cell won't have null values. In that case, you can try manually setting the Value property of the GridDataCellElement, which internally will set the Value of the GridViewCellInfo. Here is what I have in mind:

. . . . . .
switch (r.Result)
{
	case Quality.OK:

		Text = r.Value.ToString("F1");

        if (this.Value == null)
        {
            this.Value = r.Value.ToString("F1");
        }
        else if (this.Value.ToString() != r.Value.ToString("F1"))
        {
            this.Value = r.Value.ToString("F1");
        }

        BackColor = Color.Lime;
		NumberOfColors = 1;
		DrawFill = true;
		break;
. . . . . .
Keep in mind that setting the Value property will trigger the SetContentCore() again. I think I handled that case and avoided the StackOverflow exception. However, I would suggest carefully testing the code and avoiding a recursive call. Using the above code I can get the value now using the following syntax:

var r0 = rgv.Rows[0].Cells["colR0"].Value;
var r1 = rgv.Rows[0].Cells["colR1"].Value;
var r2 = rgv.Rows[0].Cells["colR2"].Value;
var r3 = rgv.Rows[0].Cells["colR3"].Value;
var r4 = rgv.Rows[0].Cells["colR4"].Value;
I am attaching the modified version of your project. I hope that this approach will work for you.

Marian
Top achievements
Rank 2
Bronze
Iron
Iron
commented on 02 Jan 2026, 10:37 PM

Hello,

thanks, I will try it. I already found out that setting Value calls SetContentCore() recursively and throws a StackOverflow :) If this approach is acceptable, I can do it in this way, but I think it doesn't look very clean. If there's a chance of null values, or generally in case of some error, I could store the value in RowInfo.Cells[ColumnInfo.Index].Tag and only set Value if it differs. That might work, right?

Another idea, what if I wrote Result[10] into FieldName and use a dynamic type with logic in TryGetMember? That could also work, although performance would be probably poor. Or maybe, implement a custom ICustomTypeDescriptor and handle special property names there?

If you have a feature request list or some other feedback list, could you please consider adding support for overriding GetValue / SetValue? If it's not too big problem, it could be useful. I'm not sure how to do it in Telerik GridView environment, I know visual cells are separated from the data, maybe a column could have a flag indicating it wants this behavior, and the GridViewCellInfo could call an override or event on the column if needed?

Dinko | Tech Support Engineer
Telerik team
commented on 07 Jan 2026, 09:43 AM

I understand it is not a perfect solution, but in this scenario, it could work.

Using the RowInfo.Cells[ColumnInfo.Index].Tag property could potentially be used here. The this.RowInfo.Cells[col.Index] will return a CellInfo object, which is unique for the visual cell data. You could store the value inside the Tag property and use it to check it in different scenarios. Keep in mind that this Tag property will also need to be changed when the value of the cell is changed by the end user or when it is changed from the datasource.

For the TryGetMember and for the ICustomTypeDescriptor, I can't say if this is going to be a good approach, and you won't hit something that cannot be worked around.  In my opinion, using these approaches will make the code more complex and hard to maintain, but you can still give it a try. 

At the moment of writing, we don't have plans to expose a way to override the code that retrieves a cell's value. Nevertheless, I will forward your request to our development team for consideration. 

If you have anyother questions feel free to contact us again in a new thread and we will be happy to help.

Tags
GridView
Asked by
Marian
Top achievements
Rank 2
Bronze
Iron
Iron
Answers by
Dinko | Tech Support Engineer
Telerik team
Share this question
or