Under classic (COM-based) ASP, web developers were required to
manually repopulate the values of the incoming form widgets during the
process of constructing the outgoing HTTP response. For example, if the
incoming HTTP request contained five text boxes with specific values,
the *.asp file required script code to extract the current values (via the Form or QueryString collections of the Request
object) and manually place them back into the HTTP response stream.
Needless to say, this was a drag. If the developer failed to do so, the
caller was presented with a set of five empty text boxes!
Under ASP.NET, we no longer
have to manually scrape out and repopulate the values in the HTML
widgets because the ASP.NET runtime automatically embeds a hidden form
field (named __VIEWSTATE), which
will flow between the browser and a specific page. The data assigned to
this field is a Base64-encoded string that contains a set of name/value
pairs representing the values of each GUI widget on the page at hand.
The System.Web.UI.Page base class's Init event handler is the entity in charge of reading the incoming values in the __VIEWSTATE
field to populate the appropriate member variables in the derived
class. (This is why it is risky at best to access the state of a web
widget within the scope of a page's Init event handler.)
Also, just before the outgoing response is emitted back to the requesting browser, the __VIEWSTATE
data is used to repopulate the form's widgets. Clearly, the best thing
about this aspect of ASP.NET is that it just happens without any work on
your part. Of course, you are always able to interact with, alter, or
disable this default functionality if you so choose. To understand how
to do this, let's see a concrete view state example.
1. Demonstrating View State
First, create a new Empty Web Site called ViewStateApp and insert a new Web Form. On your .aspx page, add asingle ASP.NET ListBox web control named myListBox and a single Button control named btnPostback. Handle the Click event for the Button to provide a way for the user to post back to theweb server:
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
}
protected void btnPostback_Click(object sender, EventArgs e)
{
// No-op. This is just here to allow a postback.
}
}
Now, using the Visual Studio 2010 Properties window, access the Items property and add four ListItems to the ListBox using the associated dialog box. The resulting markup looks like this:
<asp:ListBox ID="myListBox" runat="server">
<asp:ListItem>Item One</asp:ListItem>
<asp:ListItem>Item Two</asp:ListItem>
<asp:ListItem>Item Three</asp:ListItem>
<asp:ListItem>Item Four</asp:ListItem>
</asp:ListBox>
Note that you are hard-coding the items in the ListBox directly within the *.aspx file. As you already know, all <asp:>
definitions in an HTML form will automatically render back their HTML
representation before the final HTTP response (provided they have the runat="server" attribute).
The <%@Page%> directive has an optional attribute called EnableViewState that by default is set to true. To disable this behavior, simply update the <%@Page%> directive as follows:
<%@ Page Language="C#" AutoEventWireup="true"
CodeFile="Default.aspx.cs" Inherits="_Default"
EnableViewState ="false" %>
So, what exactly does it
mean to disable view state? The answer is, it depends. Given the
previous definition of the term, you would think that if you disable
view state for an *.aspx file, the values in your ListBox
would not be remembered between postbacks to the web server. However,
if you were to run this application as is, you might be surprised to
find that the information in the ListBox is retained regardless of how many times you post back to the page.
In fact, if you examine the
source HTML returned to the browser (by right-clicking the page within
the browser and selecting View Source), you may be further surprised to
see that the hidden __VIEWSTATE field is still present:
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE"
value="/wEPDwUKLTM4MTM2MDM4NGRkqGC6gjEV25JnddkJiRmoIc10SIA=" />
However, assume that your ListBox is dynamically populated within the code-behind file rather than within the HTML <form> definition. First, remove the <asp:ListItem> declarations from the current *.aspx file:
<asp:ListBox ID="myListBox" runat="server">
</asp:ListBox>
Next, fill the list items within the Load event handler in your code-behind file:
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
// Fill ListBox dynamically!
myListBox.Items.Add("Item One");
myListBox.Items.Add("Item Two");
myListBox.Items.Add("Item Three");
myListBox.Items.Add("Item Four");
}
}
If you post to this updated page, you'll find that the first time the browser requests the page, the values in the ListBox are present and accounted for. However, on postback, the ListBox
is suddenly empty. The first rule of ASP.NET view state is that its
effect is only realized when you have widgets whose values are
dynamically generated through code. If you hard-code values within the *.aspx file's <form> tags, the state of these items is always remembered across postbacks (even when you set EnableViewState to false for a given page).
If the idea of disabling view state for the entire *.aspx file seems a bit too aggressive, know that every descendent of the System.Web.UI.Control base class inherits the EnableViewState property, which makes it very simple to disable view state on a control-by-control basis:
<asp:GridView id="myHugeDynamicallyFilledGridOfData" runat="server"
EnableViewState="false">
</asp:GridView>
NOTE
Under .NET 4.0, large view state data values are automatically compressed, to help reduce the size of this hidden form field.
2. Adding Custom View State Data
In addition to the EnableViewState property, the System.Web.UI.Control base class provides a protected property named ViewState. Under the hood, this property provides access to a System.Web.UI.StateBag type, which represents all the data contained within the __VIEWSTATE field. Using the indexer of the StateBag type, you can embed custom information within the hidden __VIEWSTATE form field using a set of name/value pairs. Here's a simple example:
protected void btnAddToVS_Click(object sender, EventArgs e)
{
ViewState["CustomViewStateItem"] = "Some user data";
lblVSValue.Text = (string)ViewState["CustomViewStateItem"];
}
Because the System.Web.UI.StateBag type has been designed to operate on System.Object
types, when you wish to access the value of a given key, you should
explicitly cast it into the correct underlying data type (in this case, a
System.String). Be aware, however, that values placed within the __VIEWSTATE field cannot literally be any object. Specifically, the only valid types are Strings, Integers, Booleans, ArrayLists, Hashtables, or an array of these types.
So, given that .aspx pages can insert custom bits of information into the __VIEWSTATE
string, the next logical question is when you would want to do so. Most
of the time, custom view-state data is best suited for user-specific
preferences. For example, you may establish view-state data that
specifies how a user wishes to view the UI of a GridView
(such as a sort order). However, view-state data is not well-suited for
full-blown user data, such as items in a shopping cart or cached DataSets.
When you need to store this sort of complex information, you must work
with session or application data. Before we get to that point, though,
you need to understand the role of the Global.asax Global.asax file.