Reducing viewstate on custom controls

One of the biggest waste I see in using viewstate is that it persists values that are already set in the aspx. These get set every time that the page runs and don’t need to be round tripped to the browser so that they will be persisted.

I did a little experiment a while back where I tried using custom attributes to save viewstate on controls. I found that while it was very handy being able to save properties in viewstate without having to write repetitive code, a much bigger advantage was I now had one place where I could optimize exactly what went into it.

The problem with persisting values in viewstate that are already saved in the aspx is pretty easy to solve. The code just needs to keep track of what the values of the properties were after the controls in aspx page are initialized:

  1. protected override void OnInit(System.EventArgs e)
  2. {
  3.  base.OnInit(e);
  4.  List<propertyinfo> viewstateProperties = GetViewStateProperties();
  5.  foreach (PropertyInfo property in viewstateProperties)
  6.   _initialValues[property.Name] = property.GetValue(this, null);
  7. }
  8.  
  9. private List</propertyinfo><propertyinfo> GetViewStateProperties()
  10. {
  11.  List</propertyinfo><propertyinfo> properties = new List</propertyinfo><propertyinfo>();
  12.  foreach (PropertyInfo property in this.GetType().GetProperties())
  13.  {
  14.   object[] attributes = property.GetCustomAttributes(typeof(PersistToViewState), true);
  15.   if (attributes.Length > 0)
  16.    properties.Add(property);
  17.  }
  18.  return properties;
  19. }
  20. </propertyinfo>

Then when it’s time to save the viewstate, check whether the value has actually changed:

  1. protected override object SaveViewState()
  2. {
  3.  // Save automatic viewstate properties
  4.  List<propertyinfo> viewstateProperties = GetViewStateProperties();
  5.  for (int x = 0; x < viewstateProperties.Count; x++)
  6.  {
  7.   PropertyInfo property = viewstateProperties[x];
  8.   object value = property.GetValue(this, new object[] { });
  9.   if (_initialValues.ContainsKey(property.Name))
  10.   {
  11.    // Only save the property to viewstate if it has changed since viewstate
  12.    // was loaded. If it hasn't been changed, we don't need to store it because
  13.    // it will be loaded from the aspx on the next page load
  14.    object initialValue = _initialValues[property.Name];
  15.    if (!object.Equals(initialValue, value))
  16.     ViewState[x.ToString()] = value;
  17.   }
  18.   else
  19.    ViewState[x.ToString()] = value;
  20.  }
  21.  
  22.  return base.SaveViewState();
  23. }

Complete example

It makes a big difference to how much viewstate is saved. Here’s the viewstate of a single example Pie control before this simple bit of optimization:

  1. <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
  2. value="/wEPDwUJMzUzODE3ODMxD2QWAgIDD2QWAgIBDxYEHgEwBQlhcHBsZSBwaWUe
  3. ATECA2Rk/V7Nc8+xuVAMsS4R9hLs1SHD4Js=" />

Here’s the viewstate after:

  1. <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
  2. value="/wEPDwUJMzUzODE3ODMxZGTnlfN1SaJSDVcRTDQPIBVe1fK+Gw==" />

Everything else still works as expected. Controls created entirely in code will still work perfectly well, the only difference will be that all properties that are set will need to be persisted so you won’t see any reduction in the amount of viewstate used there. The properties set in code of controls created in the aspx will also be saved to viewstate so they will be persisted after the next postback.

Posted on 01 Feb 09 by Helen Emerson (last updated on 01 Feb 09).
Filed under ASP.NET, Server controls