Control State vs. View State Example

This example shows how to create a custom control named IndexButton that uses control state to maintain critical state information across page requests. Control state, introduced in ASP.NET version 2.0, is similar to view state but functionally independent of view state. A page developer can disable view state for the page or for an individual control for performance. However, control state cannot be disabled. Control state is designed for storing a control's essential data (such as a pager control's page number) that must be available on postback to enable the control to function even when view state has been disabled. By default, the ASP.NET page framework stores control state in the page in the same hidden element in which it stores view state. Even if view state is disabled, or when state is managed using Session, control state travels to the client and back to the server in the page. On postback, ASP.NET deserializes the contents of the hidden element and loads control state into each control that is registered for control state.

Note

Use control state only for small amounts of critical data that are essential for the control across postbacks. Do not use control state as an alternative to view state.

The example illustrates a custom control that saves state in both control state and view state. In the example, the IndexButton control derives from the Button class and defines an Index property that it saves in control state. For comparison, IndexButton also defines an IndexInViewState property that it stores in the ViewState dictionary. To see the difference between control state and view state, use the IndexButton control as demonstrated in the .aspx page listed in the "Test Page for the IndexButton Control" section later in this topic.

Code Listing for the IndexButton Control

' IndexButton.vbOption Strict OnImports System
Imports System.ComponentModel
Imports System.Security.Permissions
Imports System.Web
Imports System.Web.UI
Imports System.Web.UI.WebControls

Namespace Samples.AspNet.VB.Controls
    < _
    AspNetHostingPermission(SecurityAction.Demand, _
        Level:=AspNetHostingPermissionLevel.Minimal), _
    AspNetHostingPermission(SecurityAction.InheritanceDemand, _
        Level:=AspNetHostingPermissionLevel.Minimal), _
    ToolboxData("<{0}:IndexButton runat=""server""> </{0}:IndexButton>") _
    > _
    PublicClass IndexButton
        Inherits Button
        Private indexValue AsInteger

        < _
        Bindable(True), _
        Category("Behavior"), _
        DefaultValue(0), _
        Description("The index stored in control state.") _
        > _
        PublicProperty Index() AsIntegerGetReturn indexValue
            EndGetSet(ByVal value AsInteger)
                indexValue = value
            EndSetEndProperty

        < _
        Bindable(True), _
        Category("Behavior"), _
        DefaultValue(0), _
        Description("The index stored in view state.") _
        > _
        PublicProperty IndexInViewState() AsIntegerGetDim obj AsObject = ViewState("IndexInViewState")
                If obj IsNothingThen obj = 0
                ReturnCInt(obj)
            EndGetSet(ByVal value AsInteger)
                ViewState("IndexInViewState") = value
            EndSetEndPropertyProtectedOverridesSub OnInit(ByVal e As EventArgs)
            MyBase.OnInit(e)
            Page.RegisterRequiresControlState(Me)
        EndSubProtectedOverridesFunction SaveControlState() AsObject            ' Invoke the base class's method and            ' get the contribution to control state            ' from the base class.            ' If the indexValue field is not zero            ' and the base class's control state is not null,            ' use Pair as a convenient data structure            ' to efficiently save             ' (and restore in LoadControlState)            ' the two-part control state            ' and restore it in LoadControlState.Dim obj AsObject = MyBase.SaveControlState()

            If indexValue <> 0 ThenIf obj IsNotNothingThenReturnNew Pair(obj, indexValue)
                ElseReturn indexValue
                EndIfElseReturn obj
            EndIfEndFunctionProtectedOverridesSub LoadControlState(ByVal state AsObject)
            If (state IsNotNothing) ThenDim p As Pair = TryCast(state, Pair)
                If p IsNotNothingThenMyBase.LoadControlState(p.First)
                    indexValue = CInt(p.Second)
                ElseIf (TypeOf (state) IsInteger) Then
                        indexValue = CInt(state)
                    ElseMyBase.LoadControlState(state)
                    EndIfEndIfEndIfEndSubEndClassEndNamespace
// IndexButton.csusing System;
using System.ComponentModel;
using System.Security.Permissions;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace Samples.AspNet.CS.Controls
{
    [
    AspNetHostingPermission(SecurityAction.Demand,
        Level = AspNetHostingPermissionLevel.Minimal),
    AspNetHostingPermission(SecurityAction.InheritanceDemand, 
        Level=AspNetHostingPermissionLevel.Minimal),
    ToolboxData("<{0}:IndexButton runat=\"server\"> </{0}:IndexButton>")
    ]
    publicclass IndexButton : Button
    {
        privateint indexValue;

        [
        Bindable(true),
        Category("Behavior"),
        DefaultValue(0),
        Description("The index stored in control state.")
        ]
        publicint Index
        {
            get
            {
                return indexValue;
            }
            set
            {
                indexValue = value;
            }
        }

        [
        Bindable(true),
        Category("Behavior"),
        DefaultValue(0),
        Description("The index stored in view state.")
        ]
        publicint IndexInViewState
        {
            get
            {
                object obj = ViewState["IndexInViewState"];
                return (obj == null) ? 0 : (int)obj;
            }
            set
            {
                ViewState["IndexInViewState"] = value;
            }
        }

        protectedoverridevoid OnInit(EventArgs e)
        {
            base.OnInit(e);
            Page.RegisterRequiresControlState(this);
        }

        protectedoverrideobject SaveControlState()
        {
            // Invoke the base class's method and// get the contribution to control state// from the base class.// If the indexValue field is not zero// and the base class's control state is not null,// use Pair as a convenient data structure// to efficiently save // (and restore in LoadControlState)// the two-part control state// and restore it in LoadControlState.object obj = base.SaveControlState();

            if (indexValue != 0)
            {
                if (obj != null)
                {
                    returnnew Pair(obj, indexValue);
                }
                else
                {
                    return (indexValue);
                }
            }
            else
            {
                return obj;
            }
        }

        protectedoverridevoid LoadControlState(object state)
        {
            if (state != null)
            {
                Pair p = state as Pair;
                if (p != null)
                {
                    base.LoadControlState(p.First);
                    indexValue = (int)p.Second;
                }
                else
                {
                    if (state isint)
                    {
                        indexValue = (int)state;
                    }
                    else
                    {
                        base.LoadControlState(state);
                    }
                }
            }
        }

    }
}

Code Discussion

The implementation of the IndexButton control illustrates the three tasks that you must perform to enable a control to participate in control state:

  • Override the OnInit method and invoke the RegisterRequiresControlState method to register with the page for participation in control state. This must be done with each request.

  • Override the SaveControlState method to save data in control state.

  • Override the LoadControlState method to load data from control state. This method calls the base class method and gets the base class's contribution to control state. If the indexValue field is not zero and the base class's control state is not null, the Pair class is used as a convenient data structure to save and restore the two-part control state.

Test Page for the IndexButton Control

The following example illustrates a page that disables view state by setting the EnableViewState attribute to false in the @ Page directive. The page uses the IndexButton control and adds 1 to the values of the Index and IndexInViewState properties of the control in the Page_Load event handler. The labels in the page display the values of the Index and IndexInViewState properties.

Because the Index property is stored in control state, which cannot be disabled, the Index property maintains its value on postback and increases by one each time the page is posted back to the server. In contrast, because the IndexInViewState property is stored in view state, which is disabled for the page, the IndexInViewState property always has its default value of zero.

<%@ Register TagPrefix="aspSample"Namespace="Samples.AspNet.vB.Controls" Assembly="Samples.AspNet.VB" %>
<%@ Page Language="VB" Trace="true" EnableViewState="false" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
    Sub Page_Load(ByVal sender AsObject, ByVal e As EventArgs)
        Label1.Text = IndexButton1.Index.ToString()
        Label2.Text = IndexButton1.IndexInViewState.ToString()
        IndexButton1.Index += 1
        IndexButton1.IndexInViewState += 1
    EndSub
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
  <head id="Head1" runat="server">
    <title>IndexButton test page</title>
  </head>
  <body>
    <form id="form1" runat="server">
        Click the button:
        <aspSample:IndexButton Text="IndexButton" 
            ID="IndexButton1" runat="server"/>
      <br />
      <br />
      The value of the Index property of IndexButton is:<br />
      <asp:Label ID="Label1" Runat="server" Text="Label">
      </asp:Label>
      <br />
      <br />
      The value of the IndexInViewState property of IndexButton is:
      <br />
      <asp:Label ID="Label2" Runat="server" Text="Label">
      </asp:Label>
      <br />
    </form>
  </body>
</html>
<%@ Register TagPrefix="aspSample" 
  Namespace="Samples.AspNet.CS.Controls" Assembly="Samples.AspNet.CS" %>
<%@ Page Language="C#" Trace="true" EnableViewState="false" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN""http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<script runat="server">
  void Page_Load(object sender, EventArgs e)
  {
    Label1.Text = (IndexButton1.Index+).ToString();
    Label2.Text = (IndexButton1.IndexInViewState+).ToString();
  }
</script>
<html xmlns="http://www.w3.org/1999/xhtml" >
  <head id="Head1" runat="server">
    <title>IndexButton test page</title>
  </head>
  <body>
    <form id="form1" runat="server">
        Click the button:
        <aspSample:IndexButton Text="IndexButton" 
            ID="IndexButton1" runat="server"/>
      <br />
      <br />
      The value of the Index property of IndexButton is:<br />
      <asp:Label ID="Label1" Runat="server" Text="Label">
      </asp:Label>
      <br />
      <br />
      The value of the IndexInViewState property of IndexButton is:
      <br />
      <asp:Label ID="Label2" Runat="server" Text="Label">
      </asp:Label>
      <br />
    </form>
  </body>
</html>

Building and Using the Example

For information about compiling and using the custom control examples, see Building the Custom Server Control Examples.

See Also

Concepts

ASP.NET View State Overview

ASP.NET State Management Recommendations

Performance Overview

Other Resources

Developing Custom ASP.NET Server Controls