FateOri
FateOri

Reputation: 79

DataGridView HeaderCell doesn't show value when it's a numeric type

Can someone explain to me why this code

dataGridView1.Rows[0].HeaderCell.Value = 10;

shows nothing while this one

dataGridView1.Rows[0].HeaderCell.Value = 10.ToString();

works correctly?

Upvotes: 3

Views: 358

Answers (1)

Jimi
Jimi

Reputation: 32278

This looks like a sort of bug. It's actually not, but it feels like one when you set the property and you have different results in similar conditions.

What happens:
[DataGridView].Rows[N].HeaderCell is of type DataGridViewRowHeaderCell. The FormattedValue property doesn't belong to this class directly, it's derived from DataGridViewHeaderCell which, in turn, derives it from DataGridViewCell (the generic class).

The DataGridViewHeaderCell sets the default FormattedValue and Value types as:

 private static Type defaultFormattedValueType = typeof(System.String);
 private static Type defaultValueType = typeof(System.Object);

When we set:

[DataGridView].Rows[N].HeaderCell.Value = 10;

The Value is stored as an Object, boxing an int.
If we inspect the HeaderCell object right after setting the Value, we can see that the FormattedValue and FormattedValueType properties are set, respectively, to "10" and System.String.
This happens because these properties values are retrieved by the DataGridViewCell class, calling its GetFormattedValue() method.
The FormattedValueType property defaults to System.String.

So, it looks like this FormattedValue will be used when the Header content is rendered.
This doesn't happen. When the Header needs to be rendered, the DataGridViewRowHeaderCell sets its FormattedValue to the original Value (an int): the FormattedValueType is ignored. The reason is explained, in comments, in the GetContentBounds() method (and in more than one other places):

Intentionally not using GetFormattedValue because header cells don't typically perform formatting.
The content bounds are computed on demand
[...]

For the same reason, the Row Header cell doesn't raise CellFormatting events.

The private PaintPrivate() method, which renders the Row Header cell, receives a formattedValue representing an int, not a string.

It then uses:

string formattedString = formattedValue as string;

to convert formattedValue to a string. Since formattedValue is not a string, the cast returns null, so no content is rendered.

The same condition, of course, applies to the DataGridViewCellPaintingEventArgs: when the CellPainting event is raised, the e.FormattedValue property returns 10 (an int).

It return "10" instead, if we set:

[DataGridView].Rows[N].HeaderCell.Value = "10";  // Or 10.ToString()

In this case, string formattedString = formattedValue as string; does return a string and this content is rendered. Now that it's rendered, we can better understand why we cannot use the RowHeader Cell value to display content in this type of Cell: the content bounds are not exactly considered and the rendering is miserable.

To number the Rows using the Row Header, we need to handle the CellPainting event.

Upvotes: 3

Related Questions