Introduction
This article shows you how to reduce size of ViewState data generated by DataGrid
control, while maintaining all DataGrid's functionality like selecting items or paging.
I assume that you know what it's and how to use ViewState. If not, please first learn about them, e.g. read article Taking a Bite Out of ASP.NET ViewState. In that article you also find general information about reducing size of ViewState.
Background
When I started to learn and use ASP.NET in practice, one of my problems was ViewState - usually it's too large! Usually I can reduce its size, by disabling it for selected controls or for whole page. Unfortunately, when I disable VS for control, sometime I lost some of its functionality. This is true for DataGrid
- when you disable ViewState, you'll lose advanced functions like selecting items or paging. On the other side, ViewState generated by DataGrid
is usually very large - it grows with every added row and column.
In last two weeks I decided to finally solve this problem. I assumed that DataGrid
store copy of my DataTable in ViewState, or my data is stored column-by-column by BoundColumn
objects. As I found later, it was a wrong assumption.
First I displayed the whole tree of controls used to display my page. First surprise - DataGrid
uses internally other controls to display is contents. Here is sample control tree for 2x2 DataGrid
:
DataGrid
DataGridTable
DataGridItem
DataGridItem
DataGridTable
is the class derived from System.Web.UI.WebControls.Table
. DataGridItem
objects represents rows, and TableCell
represents cells of DataGrid
.
Next I decompiled .NET binaries using Reflector [^] and I browsed code of DataGrid
, BaseDataList
(its base class), BoundColumn
, and DataGridColumn
classes. Unfortunately I found nothing that may help me solve my problem.
In this point, I decided to try to decode and analyze contents of my ViewState. I used ViewState Decoder [^] for this. When I displayed my ViewState's data, its structure looked familiar for me. After wards, I found that - it's similar to control tree on my page! And I think - what if I disable ViewState for each row of DataGrid
? I checked this and... Eureka! That's it!
How to reduce size of DataGrid's ViewState - summary
Disable ViewState
If you can, disable the ViewState for whole DataGrid
(set property EnableViewState
to false
), or better for whole page. If you need advanced DataGrid
's functionality like selecting items or paging, you can't use this method. In this case you should use methods listed below.
Disable columns autogeneration
Set property AutoGenerateColumns
to false
, and create columns manually using Property Builder for DataGrid
control. When columns are generated automatically, information about them is stored in the ViewState.
Disable ViewState for each DataGrid's row
This is the best method, because copy of displayed data is stored in ViewState as values of TableCell.Text
property. When you disable that, DataGrid
ViewState's size will be constant and don't increase when you display more rows. You can use the following code for this:
private void DisableViewState(DataGrid dg)
{
foreach (DataGridItem dgi in dg.Items)
{
dgi.EnableViewState = false;
}
}
private void Page_Load(object sender, System.EventArgs e)
{
MyDataGrid.DataSource = GetData();
MyDataGrid.DataBind();
DisableViewState(MyDataGrid);
}
Store required identifiers in ViewState as string[] array
When you display data for user, you need to store identifiers of displayed data. You can store them in hidden DataGrid
's column, but this is not optimal - your ids will be stored as DataGridItem
/TableCell
/string
tree (you also need to change code from previous method to disable ViewState only for selected columns). Better solution is to store ids in ViewState as string[]
array. Note: int[]
array may seem to be more appropriate, but not: data generated by LosFormatter
class is more compact for string[]
array than for int[]
array. This is caused by fact that class LosFormatter
is optimized only for string[]
arrays.
Example: You have array of three ids: 1, 2, 3. If you store them in ViewState as string[]
, you get:
@<1;2;3;>
When you store them as int[]
, you get:
@System.Int32, mscorlib, Version=1.0.5000.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089<i<1>;i<2>;i<3>;>
If you store more int[]
arrays in ViewState, all except first will be stored as following:
@50<i<1>;i<2>;i<3>;>
Alternatively you can store your ids in ArrayList
- below are data generated from ArrayList
for values stored as string
, and as int
:
l<1;2;3;>
l<i<1>;i<2>;i<3>;>
Here is an example code that creates and stores array with ids in ViewState:
DataTable dt = GetData();
MyDataGrid.DataSource = dt;
MyDataGrid.DataBind();
string[] ids = new string[dt.Rows.Count];
for (int n=0; n<dt.Rows.Count; ++n)
ids[n] = dt.Rows[n]["id"].ToString();
ViewState["ids"] = ids;
When you need to retrieve id of displayed data, use following code:
int index = MyDataGrid.SelectedIndex;
string[] ids = (string[])ViewState["ids"];
int myID = Convert.ToInt32(ids[index]);
Statistics
I created page that display a 10*10 table. Each cell contains a three digit number. Below are results of my tests:
VS - ViewState, CA - columns autogeneration.
Description |
ViewState size |
VS enabled, CA enabled |
6032 |
VS enabled, CA disabled |
5028 |
VS disabled for rows, CA disabled |
236 |
VS disabled for cells in all column except first, CA disabled |
948 |
VS disabled for rows, CA disabled, ids stored in VS as int[] |
476 |
VS disabled for rows, CA disabled, ids stored in VS as string[] |
316 |
VS disabled for rows, CA disabled, ids stored in VS as int s in ArrayList |
356 |
VS disabled for rows, CA disabled, ids stored in VS as string s in ArrayList |
316 |
As you see ViewState, size is reduced from 6032 to 316 bytes. We saved 5716 bytes, and now ViewState contains only 5.2% of the beginning data!
Points of Interest
I worked on primary method covered in this article (disable ViewState for each DataGrid's row) for almost two weeks. It's true that simplest solutions are most difficult to find.
I found also that, data stored in ViewState could be optimized. This can be done if you know how LosFormatter
works. Other method is to implement own class to replace LosFormatter
and this is the subject for other article.
History