Welcome to part 7 of my tour through ASP.NET 4.0. Today we’re going to look at what in hindsight was one of the larger design mistakes that Microsoft made when creating ASP.NET – The View State Opt-Out Model for Web Forms – and how they’ve approached a solution.

[Updated Feb 24: Added link to more in-depth tutorial on View State at end of article].

HTTP is Stateless

One of the first things you learn as a web developer is that HTTP is a stateless protocol. Requests sent over HTTP are independent of any that have gone before and retain no information about the user sending them or the state of the page (where the state is defined as the property values of the controls that make up the page’s control hierarchy.)

The issue then is how to retain such information between requests and, more significantly in this discussion, between postbacks on the same page. The standard solutions are to

  • Put it in session state
  • Put it in the URL
  • Put it in a cookie

Enter View State

Back in 2000, View State was created by Microsoft so we fledgling web developers didn’t have to worry about persisting this state information onto the next page. It was just another great feature whereby this information was base64-encoded on the server and stored in a hidden form element on the page named __VIEWSTATE. When the page was posted back, View State would be sent back with it and hey presto, instant state.

Ten years later, the benefit of using View State is still apparent but, as any ASP.NET MVC developer will tell you, it is not a necessary part of a web page. (To whit, MVC doesn’t use View State at all.) There are two main issues with View State

  1. All pages take a performance hit as the server must deal with decrypting, reading, rebuilding and re-encrypting view state with each post back of a page to the server. The exact process is described here. Pages which dynamically add controls to the page may also cause exceptions when the server cannot match the control tree in the view state to that on the page.
  2. The value in the __VIEWSTATE hidden form field can grow quite large quite quickly. Without you realising it, it could add several dozen kilobytes to your page resulting in slower transmission times to your users and back to the server when it is sent back to the server in the HTTP POST headers.

The solution to both these problems for Web Forms developers is to make sure that only the controls which need their state persisted across postbacks are included in View State and herein lies the main problem with View State that is being addressed in ASP.NET 4.0.

Opt-Out Mode vs. Opt-In Mode

From ASP.NET v1.0 to v3.5 SP1, there has only been an opt-out model for View State on a Web Forms page. This means that View State is switched on for every control on every page by default, unless you opt to disable it by setting the Page or Control’s EnableViewState property to false. The problem is that once EnableViewState is set to false for a Page for example, it is also set to false for every child control of that page, and there is no facility to override that further down the tree and have View State switched on for a control on that page.

Consider the scenario where I have several dozen form controls on a page and wish to maintain view state for just one DropDownList. I cannot set EnableViewState to false for the whole Page and then set it to True for the DropDownList. Instead I must leave EnableViewState set to true on the page and explicitly set it to false for every other control on the page as long as it isn’t a parent control for the DropDownList.  

Which isn’t too great.

Fortunately, Microsoft has heeded our cries and added an opt-in model for View State in ASP.NET 4.0. Addressing the same scenario using the opt-in model, I can indeed switch off View State for the whole Page and then re-enable it explicitly on the one DropDownList I need to maintain state for. However, I do it with a new property called ViewStateMode rather than EnableViewState.

Introducing ViewStateMode

The switch between opt-out and opt-in models is made using the new ViewStateMode property. This has three possible values.

  • Enabled (the default for a Page) : sets the page \ control to the old school opt-out mode
  • Disabled : sets the page \ control to use the new opt-in mode.
  • Inherit (the default for a user control) : sets the control to inherit the setting from its parent page \ control.

It can be set both code-behind and declaratively in markup as an attribute of a control or of the @Page directive.

<%@Page ... ViewStateMode="Disabled" %>

Let’s take an example. In the code below, we have a normal WebForms page with EnableViewState set to true by default.

<%@ Page Language="C#" 
    AutoEventWireup="true" CodeFile="ViewStateDemo.aspx.cs"
    Inherits="ViewStateDemo" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>View State Demo</title>
</head>
<body>
<form id="form1" runat="server">
<div>
  <asp:PlaceHolder runat="server" ID="plhOuter">
    <asp:Label runat="server" ID="lblOne" Text="My Initial Value" /><br />
    <asp:Panel runat="server" ID="pnlInner">
      <asp:Label runat="server" ID="lblTwo" Text="My Initial Value" />
    </asp:Panel>
  </asp:PlaceHolder>
  <br />
  <asp:Button runat="server" ID="btnGo" Text="View State Is Off" />
</div>
</form>
</body>
</html>

In the code-behind for this page, we re-set the Text properties for the two Labels and Button if the page has not posted back.

protected void Page_Load(object sender, EventArgs e)
{
  if (!IsPostBack)
  {
    lblOne.Text = "My set value";
    lblTwo.Text = "My set value";
    btnGo.Text = "View State Is On";
  }
}

When it loads, the page will load and the new Text values displayed. With View State enabled, those text values will stay the same when you click the button and post the page back to the server.

1_OnPageLoad

Now set ViewStateMode to Disabled for on the plhOuter PlaceHolder control.

<asp:PlaceHolder runat="server" ID="plhOuter" ViewStateMode="Disabled">

Now when you run the page and click the button, you’ll see that the two Labels lose their value as it is not persisted in View State. However, the Button control, which is outside the PlaceHolder still uses View State.

2_ViewStateOffOnOuter

So far, this is nothing new. We could replace the ViewStateMode property with EnableViewState set to false and get the same result. However, we could not then also set ViewStateMode to Enabled on the pnlInner Panel control and restore ViewState to the lblTwo Label control.

<asp:Panel runat="server" ID="pnlInner" ViewStateMode="Enabled">

Now run the page and click the button again to confirm that View State is now on for lblTwo. If you did try and replace ViewStateMode property with EnableViewState set to false on the plhOuter control now and hit the button, you’ll see that EnableViewState overrides ViewStateMode and produces the results in the screenshot above rather than the one below.

3_ViewStateOnForInnerPanel

The ViewStateMode property can be set on any control or on the Page itself and not just invisible container controls as we’ve seen so far. Move ViewStateMode=”Disabled” from the plhOuter control to the @Page directive.

<%@ Page ViewStateMode="Disabled" Language="C#" 
    AutoEventWireup="true" CodeFile="ViewStateDemo.aspx.cs"
    Inherits="ViewStateDemo" %>

Run the page again and you’ll see how the Button control now also loses state across a post back while lblTwo continues to retain it as ViewStateMode is still enabled on it.

4_ViewStateModeDisabledAtPageLevel

Remember : View State is not Control State

While this demo adequately shows you how ViewStateMode is inherited through the Control hierarchy of a page, it would have failed had I tried to use TextBox or DropDownList controls instead of Labels with the code-behind setting their selected value or text. The DropDownList would have retained its SelectedIndex and the TextBox its Text whether View State was enabled or not because those particular properties are stored in control state. Control state is designed for storing a control's essential data (such as a TextBox's text) that must be available on postback to enable the control to function even when view state has been disabled. Thus control state is always maintained even if EnableViewState is set to false and ViewStateMode is set to Disabled.

What We’ve Learnt

In today’s episode, we’ve seen that ASP.NET 4.0 presents a new opt-in mode for View State on a page via the new ViewStateMode property. However, we’ve also seen that setting the EnableViewState property to false will disable View State for any child control or page regardless of whether one has had ViewStateMode set to Enabled on it. Thus for any control’s state to be saved in a page’s View State, the following must be true.

  • EnableViewState must be set to true for both the Page and the Control (which is the default setting)
  • ViewStateMode must be set to Enabled on the Control.

We also noted that the setting of ViewStateMode has no influence over the workings of a Control’s Control State.

For a really good, in-depth look at how View State works, you can look at both of these:

Happy Coding!