Wednesday, September 06, 2006

How to format your GridView autogenerated columns

The GridView is nothing more than an oversized DataGrid. The internal concept is completely rewritten, as far as I see, though. For example, Columns are not called columns anymore, they are DataControlField. Well, I stumbled upon one situation where I needed to format the autogenerated columns in a GridView. Normally, you could take the Columns collection and change the DataFormatString property for each column, but autogenerated columns (or AutoGeneratedField) are not part of the Columns collection. More than that, AutoGeneratedFields are nothing more than BoundFields with lots of useless restrictions on it like being a sealed class and throwing errors when trying to change some properties (like DataFormatString).

The first step is getting hold of the autogenerated column. Luckily the GridView control has a virtual method called CreateAutoGeneratedColumn which returns a AutoGeneratedField. So create a control that inherits GridView, override this method, take the generated field in the base method, change DataFormatString. I couldn't find any way to do that except with reflection. I noticed that the value of the DataFormatString property was stored in the ViewState of the AutoGeneratedField, so I used reflection to get to it, then I changed the value.

This is the code:
protected override AutoGeneratedField CreateAutoGeneratedColumn(
AutoGeneratedFieldProperties fieldProperties)
AutoGeneratedField field =
StateBag sb = (StateBag)field.GetType()
BindingFlags.GetProperty |
BindingFlags.NonPublic |
null, field, new object[] {});
sb["DataFormatString"] = "{0:N}"; //or the format string you prefer
field.HtmlEncode=false; //see update
return field;

The BoundField object formats values differently depending on html encoding. If the html encoding is enabled, then the value is transformed into a string, then the formatting is applied. That means that number formatting will NOT work when html encoding is true.
That is why I've added the field.HtmlEncode=false; line above. A more secure option would be to make the HtmlEncode property depend on the field.DataType.

While this works, I don't recommend doing it like that. I am using it because I have custom user controls that inherit from the GridView and I need to cover the AutoGeneratedColumns=true case. It is much easier to generate your columns yourself from the DataSource using BoundField objects (Or any other DataControlField objects) that you add to the GridView.Columns collection.