Maintain State Control

Diversify the Role of the Viewstate with a Control-specific Cross-page State

Dino Esposito

October 30, 2009

10 Min Read
ITPro Today logo

CoreCoder

LANGUAGES: C#

ASP.NET VERSIONS: 1.0 | 1.1 | 2.0

 

MaintainState Control

Diversifythe Role of the Viewstate with a Control-specific Cross-page State

 

By DinoEsposito

 

Like itor not, page size affects performance. The larger the response, the more timethe page takes to download and display. To feel the difference, create anyASP.NET Web form page with a 10-row DataGrid and not many frills, such astables, images, and stylesheets. Then, run the page and count the size of onlyone of the page elements the viewstate. Well, only the viewstate of such apage is likely be more than 2KB!

 

Thetheme of this article is not pure page performance, but rather the role ofviewstate in the context of performance and reliability.

 

Oftenwrongly blamed for deficient page performance, the viewstate plays a key rolein the ASP.NET programming model: It is a secure and compact vehicle forstoring data that must survive across postbacks. The actual weight ofviewstate, however, must be evaluated with respect to other server-side statemanagement tools such as Session and Cache. Data stored in viewstate isstateful information that would otherwise stay on the server, thus taxing theWeb server s memory. The key point about viewstate is this: Take advantage ofit, but don t misuse it and never overindulge in its storage capabilities.

 

Who UsesViewstate?

Viewstatecan take at least 10 percent of the entire page size and for nothing, fromthe browser s perspective. Although most controls can work with or without viewstate,for some of them (the most complex) a disabled viewstate can be a serious issueup to the point of jeopardizing the overall behavior. The majority ofproperties that a server control provides are implemented through the ViewStatehashtable:

 

public stringText

{

 get {returnConvert.ToString(ViewState["Text"]);

 set {ViewState["Text"] = value;}

}

 

If theviewstate for a given control is disabled, the property is assigned a defaultvalue on postback. The assigned value can be the value assigned through theASPX markup, if any. If not, it will be the value assigned in the control sconstructor or in the get accessor of the property. No value programmaticallyassigned to the property is retained across postbacks. Let s consider a TextBoxcontrol.

 

Most ofthe time you simply use the Text property of the TextBox control. Interestinglyenough, this property seems to work regardless of the viewstate settings. Whendisabled, the viewstate for the TextBox is neither restored nor saved, asexpected. However, the methods of the IPostBackDataHandler interface regularlyprocess the TextBox-posted data the HTML value attribute of the tag. In doing so, the LoadPostData method copies the value to the Text propertyof the server-side TextBox control.

 

It isimportant to note that this happens only for the Text property. All the otherproperties are lost, including ReadOnly, MaxLength, colors, and borders.Drilling down a bit, though, you easily see that a disabled viewstate altersthe behavior of the control, even for what relates to the Text property. TheTextChanged event will repeatedly fire even when no real change occurred overthe postback (see the code in Figure 1). What s up?

 

boolLoadPostData(string postDataKey,

                 NameValueCollectionpostCollection)

{

 string restoredText;

 string postedText;

 // Always the empty string (or what in theASPX) with

 // the viewstate disabled

 restoredText = this.Text;

 postedText =postCollection.get_Item(postDataKey);

 // Whenever the input differs from the emptystring

 // (or what in the ASPX), the event fires.Repeatedly.

 if (restoredText != postedText) {

    this.Text = postedText;

    return true;  // Fires TextChanged

 }

 return false;

}

Figure1: Disable theviewstate and the TextChanged event will fire too often.

 

When theTextBox is instantiated to serve a new request for the page (i.e., a postback),properties get default values set in the constructor or values assigned throughthe ASPX markup. With a disabled viewstate, this is what happens to therestoredText variable shown in Figure 1. The first time a different value isposted, the TextChanged event fires as expected. From then on, though, theTextChanged event fires at each postback because the strings compared in Figure1 are always different. A restored viewstate would guarantee a constant andconsistent update of the restoredText variable on each postback.

 

Allstandard 1.x ASP.NET controls are designed to minimize the impact of a disabledviewstate. In general, though, disabling the viewstate to get pages to downloadfaster may introduce harder to solve problems than just slow rendering.Disabling the viewstate can break the functionality of certain components,especially custom and third-party controls that make undocumented use of theviewstate. More often than not, controls make a private use of the viewstate,meaning that they store in the viewstate private or protected propertiescompletely invisible and not programmable from within the page level. If thisis the case, there s no way out other than re-enabling the viewstate. Be awareof this problem you leave to your customers when you design and code customcontrols for ASP.NET 1.x.

 

InASP.NET 2.0, this problem finds a definitive solution with the introduction of thecontrol state. In ASP.NET 1.x you can simulate the version 2.0 control statethrough a custom hidden field.

 

IntroducingControl State

How doescontrol state differ from viewstate? In ASP.NET 2.0, the control state is asubset of the viewstate. You should use control state only for small amounts ofcritical data that is essential for the functioning of a control acrosspostbacks. In no way should the control state be used as an alternative toviewstate. Control state and viewstate are stored in the same hidden field(__VIEWSTATE) and are managed through pairs of overridable functions. However,unlike the viewstate, the control state can t be disabled. This guarantees thatno pages risk being broken by a disabled viewstate. The control state iscompletely handled by the proprietary control and, in a certain way, the control state is the control s private viewstate.

 

If yourcustom control has private or protected properties stored in the viewstate, youshould move all of them to the control state in ASP.NET 2.0. Anything you storein the control state remains there until it is explicitly removed. The moredata you pack into it, the more data is moved back and forth between thebrowser and the Web server. But in doing so, you gain total control over the workingof the component in any host page and you can deliver to your customers a solidcomponent that is not a time bomb or a Trojan horse.

 

Control State in ASP.NET 2.0

InASP.NET 1.x, the viewstate comes with a minimal, but effective, programminginterface. All controls come with a ViewState property, a customized hashtable(StateBag class) where you explicitly store information. The content of thecollection is serialized and secured when the page gets to generate thebrowser s response. The viewstate originates a Base64-encoded hidden field.Upon page loading, the hidden field is decoded, deserialized, and remapped tocontrol properties. You can control the serialization and deserializationprocess through a pair of overridable methods: LoadViewState and SaveViewState.

 

Theimplementation of control state develops along the same lines, with one notabledifference: There s no global container for control state data like theStateBag class. In other words, you can t rely on, say, a ControlStatecollection object; you can use any variable type and name you like to storedata during the page request. Two overridable methods offer a chance todeserialize and serialize data from and to an output stream. The methods areLoadControlState and SaveControlState (see Figure 2).

 

public classMyButton : Button

 {

 private int m_counter;

 public int Counter

 {

   get {return m_counter;}

   set {m_counter = value;}

 }

 protected override void OnInit(EventArgs e)

{

   base.OnInit(e);

   Page.RegisterRequiresControlState(this);

 }

 protected override voidLoadControlState(object state)

 {

   if (state != null)

 {

    Pair p = state as Pair;

    if (p != null)

    {

      base.LoadControlState(p.First);

     _m_counter = (int) p.Second;

    }

    else

    {

      if (state is int)

        _ m_counter = (int) state;

      else

          base.LoadControlState(state);

    }

  }

}

 protected override object SaveControlState()

 {

   object obj = base.SaveControlState();

   if (m_counter != 0)

   {

     if (obj != null)

       return new Pair(obj, _index);

     else

       return (m_counter);

   }

   else

     return obj;

  }

}

Figure2: A customcontrol that implements control state.

 

TheMyButton control illustrated in Figure 2 has a public property named Counterdesigned to bufferize the number of times the control is clicked. If you storethis value in the viewstate, you re lost as soon as the page developer turnsviewstate support off. Before you implement the control state, you must firstrequire ad hoc support from the page infrastructure:

 

protectedoverride void OnInit(EventArgs e)

{

 base.OnInit(e);

 Page.RegisterRequiresControlState(this);

}

 

TheRegisterRequiresControlState method adds the control to the list of controls inthe page that implements control state. The list is scanned when the page isgoing to load and save state from the body of the HTTP request. If the controlis in the list, the LoadControlState method is invoked. As demonstrated inFigure 2, the method receives a Pair object where the first element is the restof the state, and the second element is what the control itself stored inSaveControlState. If your control needs to persist more than one piece ofinformation you can group them in an array and store the array. The controlstate is serialized as a special section within the __VIEWSTATE hidden field.

 

To fullyunderstand control state, and the risk run by 1.x pages, consider that inASP.NET 2.0 the DataList control stores SelectedIndex and EditItemIndex in thecontrol state.

 

Control State in ASP.NET 1.x

WithASP.NET 2.0 Beta 1 available, most developers have already begun to seriouslyapproach the new platform, which is now only a few months away from release.With 2.0 providing an off-the-shelf solution, I m not sure if I would spend anytime fixing the issue for 1.x controls. However, the work is not hard and canbe outlined in two steps.

 

First,take your few critical properties out of the viewstate and implement themthrough protected or private properties. Next, write a couple of methods thatserialize and deserialize those properties into a string. You can use anystring format you like. These two methods, say LoadMyControlState andSaveMyControlState, will plug into the control infrastructure and work bystreamlining the content to and from a custom hidden field. You can callLoadMyControlState from within the OnLoad event and SaveMyControlState fromwithin OnPreRender. Figure 3 shows the source code of a TextBox that retainsReadOnly and BackColor, even with the viewstate disabled. By using theLosFormatter ASP.NET class you can encode your data like the viewstate.

 

public classTextBox : System.Web.UI.WebControls.TextBox

{

 const string CONTROLSTATE ="__CONTROLSTATE";

 protected virtual void LoadMyControlState()

 {

   string ctlState;

   ctlState = Page.Request.Form[CONTROLSTATE];

   if (controlState == null)

      return;

   LosFormatter f = new LosFormatter();

   object state = f.Deserialize(controlState);

   object[] arrValues = (object[]) state;

   ReadOnly = (bool) arrValues[0];

   BackColor = (Color) arrValues[1];

 }

 protected virtual void SaveMyControlState()

 {

   object[] state = new object[2];

   state[0] = this.ReadOnly;

   state[1] = this.BackColor;

   LosFormatter f = new LosFormatter();

   StringWriter writer = new StringWriter();

   f.Serialize(writer, state);

   Page.RegisterHiddenField(CONTROLSTATE,

      writer.ToString());

 }

 protected override void OnLoad(EventArgs e)

 {

   base.OnLoad (e);

   LoadMyControlState();

 }

 

 protected override void OnPreRender(EventArgse)

{

   base.OnPreRender (e);

   SaveMyControlState();

 }

}

Figure3: A TextBox thatimplements control state.

 

Conclusion

Don tblindly kill the viewstate; and especially don t do it if you re usingthird-party controls including your own team s controls. Aim at controllingthe overall page size and define a target size that is both reasonable and inline with your application s expectations. A page size ranging from 20K to 30K ismore than acceptable; however, you can even go a little over if the applicationis particularly complex.

 

Be awarethat viewstate is a key part of the ASP.NET infrastructure; you can limit itssize and even kill it, but never blindly. Introduced in ASP.NET 2.0, thecontrol state gives you more flexibility you can serialize it at will andkeeps your controls out of the page developer s decisions about the overallpage viewstate.

 

DinoEsposito is aWintellect trainer and consultant who specializes in ASP.NETand ADO.NET. Author of Programming Microsoft ASP.NET and Introducing ASP.NET2.0, both fromMicrosoft Press, Dino has also helped several companies architect and buildeffective products for ASP.NET developers. Dino is the cofounder of http://www.VB2TheMax.com, a popular portalfor VB and .NET programmers. Write to him at mailto:[email protected] or jointhe blog at http://weblogs.asp.net/despos.

 

 

 

Sign up for the ITPro Today newsletter
Stay on top of the IT universe with commentary, news analysis, how-to's, and tips delivered to your inbox daily.

You May Also Like