Page States

At the heart of ASP.NET are new ways of designing and processing forms. Within this Web Forms framework, a new set of server controls are provided as replacements for conventional XHTML form tags. As noted, the primary functionality provided by these controls is their direct accessibility by server scripts. A second important feature is that form controls retain their values between page postings.

By way of illustration, enter any text characters into the following textbox and click the "Submit" button.


  
Figure 2-35. Repopulation of form controls with entered data.

Notice that after the page is posted back to the server, the typed text reappears in the textbox. Under conventional forms processing, special coding is needed to cause this field to redisplay entered data; normally, the textbox is returned empty. Under ASP.NET, though, repopulation of controls with entered values happens automatically.

There is an important practical outcome associated with this seemingly trivial effect. For instance, you may have visited sites with forms to fill out—survey forms, membership forms, purchasing forms, and the like. After submitting the form with missing or incomplete information, it is returned for correction. Often, the form comes back blank; the information you entered has disappeared and you have to start over again, even if only one or two entry areas are in need of correction. This can be a real irritation. Return of an empty form is common since it requires extra programming effort to repopulate the returned form with entered information. Under ASP.NET, this repopulation is automatic.

The Page View State

Repopulation of form controls with submitted values occurs through the page's View State. The View State is the status of the page when it is submitted to the server. ASP.NET maintains this status through a hidden field that it places on pages containing form controls. If you take a look at your browser's source listing for a form page, you will see this hidden field, named "__VIEWSTATE", and its encoded value which looks something like the following:

<form name="_ctl0" method="post" action="page.aspx" id="_ctl0">
  <input type="hidden" name="__VIEWSTATE"
    value="dDwtMTI3OTMzNDM4NDs7PqIp6fnWsFyownq1sZyOLgKFOBwj"/>
  ...
</form>
Listing 2-41. ASP.NET View State hidden on a Web page.

The encrypted value represents the status of the page when it is posted to the server. You do not have to interpret or understand the coded value. ASP.NET uses it behind the scenes to reconstitute the form in its submitted format when it is returned to the browser.

Maintaining a View State is the default setting for ASP.NET forms. It can be overridden for an entire page by including the attribute <%@ Page EnableViewState="False" %> in the page's Page directive; it can be disabled for any particular control by including the property setting EnableViewState="False" within the control. As an illustration of the need to override the page's View State, play along with the following scenario. Below is the XHTML and script for a set of server controls to permit data entry and display of entered data.

<SCRIPT Runat="Server">

Sub Check_Value(Src As Object, Args As EventArgs)
  If Color.Text <> "blue" Then
    ErrMessage.Text = "ERROR! Please enter the correct word!"
  End If
End Sub

</SCRIPT>

Please enter the word "blue": <br/>
<asp:TextBox id="Color" Runat="Server"/>
<asp:Button Text="Submit" OnClick="Check_Value" Runat="Server"/>
<asp:Label id="ErrMessage" ForeColor="#FF0000" Runat="Server"/>
Listing 2-42. Code that maintains the page's View State.

First, enter the word "red" (without the quotes) into the textbox to generate an error message.


Please enter the word "blue":
Figure 2-36. Maintaining the page's View State.

Now, replace "red" with the correct word "blue" and resubmit the form.

Oops, you still get the error message! This is because the View State of this page is maintained between page posts. The Label control that displays the error message automatically retains its previous value when the page is posted back.

This Label control, however, should not retain its previous value. It should be cleared of its error message between page postings so that it returns empty if the correct entry is made, or it returns with an entirely new error message if the wrong entry is made. Therefore, the default View State of this particular control needs to be overridden by recoding the Label with EnableViewState="False".

<asp:Label id="ErrMessage" ForeColor="#FF0000" Runat="Server"
  EnableViewState="False"/>
Listing 2-43. Code to override the page's View State for a control.

Now, the error message will not reappear since the control does not take part in the page's View State. The control is refreshed to its normal empty state each time the page is reloaded.

Maintaining Script Values on Post Back

All server controls, by default, take part in a page's View State. They retain their values between page postings. Other data values do not. A script variable, for example, is not part of the page's View State. Its value is reinitialized each time the page loads.

The following application is a first attempt to increment a counter each time a button is clicked. A global variable named Counter is established when the page loads. It is declared as global since it is referenced in two different subprograms. The first time the page loads (Not Page.IsPostBack), Counter is initialized to 1. Then, on each click of the button, subprogram Add_To_Counter is called to add 1 to the Counter variable and to display its incremented value.

<SCRIPT Runat="Server">
Dim Counter As Integer

Sub Page_Load

  If Not Page.IsPostBack Then
    Counter = 1
    CounterOut.Text = Counter
  End If

End Sub

Sub Add_To_Counter (Src As Object, Args As EventArgs)
  Counter += 1
  CounterOut.Text = Counter
End Sub

</SCRIPT>

<form Runat="Server">

<asp:TextBox id="CounterOut" Columns="2" Runat="Server"/>
<asp:Button Text="Add to Counter" OnClick="Add_To_Counter" Runat="Server"/>

</form>
Listing 2-44. Code for first attempt to increment a counter.

Click the following button to increment the counter by calling the Add_To_Counter subprogram.


Figure 2-37. First attempt to increment a counter.

It doesn't work! The reason is that variables are always established anew each time a page is loaded. Since they do not take part in the page's View State it is impossible to carry over a value from one page posting to the next. Counter becomes a new variable each time the page loads, and it never gets incremented past its original value.

Maintaining a script value from one page posting to the next requires that the value take part in the page's View State. Although script variables cannot take part in the page's View State, script values can be carried over by assigning them to the State Bag.

The State Bag

The State Bag is a View State data structure containing names and paired values. A value is placed into the State Bag by assigning it to a ViewState name.

ViewState("name") = "value"
Figure 2-38. General format for creating a ViewState variable.

ViewState elements work like global script variables except they take part in the page's View State. They are maintained in the same hidden field as is other View State information, and they maintain their values between page postings. You can think of ViewState("name") as a script variable that retains its value between page postings.

Below is a rewrite of the previous script, initializing the counter as ViewState("Counter") when the page is first loaded, thereby assigning it to the State Bag. On post-back, ViewState("Counter") is incremented in the Add_To_Counter subprogram by adding to its value as carried over from the previous page.

<SCRIPT Runat="Server">

Sub Page_Load

  If Not Page.IsPostBack Then
    ViewState("Counter") = 1
    CounterOut.Text = ViewState("Counter")
  End If

End Sub

Sub Add_To_Counter (Src As Object, Args As EventArgs)
  ViewState("Counter") += 1
  CounterOut.Text = ViewState("Counter")
End Sub

</SCRIPT>

<form Runat="Server">

<asp:TextBox id="CounterOut" Columns="2" Runat="Server"/>
<asp:Button Text="Add to Counter" OnClick="Add_To_Counter" Runat="Server"/>

</form>
Listing 2-45. Rewrite of code to increment a counter.

Now, click the button to increment ViewState("Counter").


Figure 2-39. Maintaining a value in View State.

It works. The incrementing value of ViewState("Counter") is carried over from one page posting to the next because it takes part in the page's View State. Of course, if you reload this page (rather than post-back this page) the ViewState variable is reinitialized to its original value.

Thus, any time you have need to create a script variable and to maintain its value between page postings, you can use a ViewState variable instead. Notice that it is not necessary to formally declare a ViewState variable or to identify its data type. It is created on-the-fly as the data type of whatever value is assigned to it. Also, ViewState variables are global in scope and can be referenced from all subprograms.

Using Server Controls to Maintain Script Values

At noted, server controls automatically take part in the page's View State. Therefore, a server control such as a TextBox can be used to hold a value between page postings, making it an alternative to a ViewState variable to maintain or increment script values. Listed below is a rewrite of the example application, this time using a TextBox, which itself is incremented each time the button is clicked.


Figure 2-40. Maintaining a value in a form control.
<SCRIPT Runat="Server">

Sub Page_Load

  If Not Page.IsPostBack Then
    CounterBox.Text = 1
  End If

End Sub

Sub Add_To_Counter (Src As Object, Args As EventArgs)
  CounterBox.Text += 1
End Sub

</SCRIPT>

<form Runat="Server">

<asp:TextBox id="CounterBox" Columns="2" Runat="Server"/>
<asp:Button Text="Add to Counter" OnClick="Add_To_Counter" Runat="Server"/>

</form>
Listing 2-46. Code to maintain a value in a form control.

When the page is initially loaded, TextBox CounterBox is given its initial value by setting its Text property to 1. When the Add_To_Counter subprogram is called, this value is available on post-back and 1 is added to it. This new value is retained between subsequent page postings, permitting the TextBox counter to be incremented.

Incidentally, it is not necessary to initialize the TextBox value in the Page_Load subprogram. Its initial value can be established by coding the Text="1" property of the TextBox itself. This property, recall, assigns a value to the TextBox for its display. Subsequent post-backs of the page increment and retain this incrementing value.

Although the above TextBox is appropriately visible in this application, you can hide controls if retained values need not or should not be viewable on the page. Below is a variation of the above application, this time maintaining the counter in a hidden TextBox control, and reporting its value in a separate Label control. In this case, the TextBox's initial value is given in its Text="1" property setting without use of a Page_Load script to set its starting value. Click the "Add to Counter" button several times, and then click the "View Counter" button.


Figure 2-41. Code to maintain a value in a hidden control.
<SCRIPT Runat="Server">

Sub Add_To_Counter (Src As Object, Args As EventArgs)
  HiddenCounter.Text += 1
End Sub

Sub View_Counter (Src As Object, Args As EventArgs)
  CounterOut.Text = "The value of counter is " & HiddenCounter.Text
End Sub

</SCRIPT>

<form Runat="Server">

<asp:TextBox id="HiddenCounter" Text="1" Visible="False" Runat="Server"/>
<asp:Button Text="Add to Counter" OnClick="Add_To_Counter" Runat="Server"/>
<asp:Button Text="View Counter" OnClick="View_Counter" Runat="Server"/>
<asp:Label id="CounterOut" Runat="Server"/>

</form>
Listing 2-47. Code to maintain a value in a hidden control.

TextBox controls, like all server controls, have a Visible="False" or Visible="True" property that can be set to hide or reveal the control. In this example, the TextBox counter need not be visible. It is used behind the scenes, much like a script variable, to store incrementing values of a counter whose value is reported through a separate Label control.

Be aware that ViewState variables and visible or hidden controls retain their values only between postings to the same page. That is, you cannot define a ViewState variable on one page and retrieve its value on a different page. However, there is a third method of retaining values between page postings which also maintains values between different pages.

Visitor Sessions

A user Session is created by ASP.NET whenever a visitor first arrives at any Web page located in the application (virtual or root) directory of a Web site. This Session object maintains identification information about the visitor, and allows ASP.NET to differentiate between different visitors as they navigate the Web site. Your visit to this tutorial site, for instance, creates your own personal Session wherein you are identified by the unique SessionID shown below. Your SessionID is a random number that is generated by ASP.NET when you first arrived at this site.

Session.SessionID = "yyruifaqbjrfafq41zao5bvx"
Figure 2-42. Your current SessionID.

Your Session remains alive for up to 20 minutes after your last interaction with a page, or until you close your browser. Thus, you can leave the site and return within this 20-minute time limit and still retain your same Session. A Session ends, though, if you remain away for longer than 20 minutes, if you are idle in your browsing for that amount of time, or if you close your browser window. A revisit to the site then generates a new Session and a new SessionID number. You are considered a new visitor.

A Session object also serves as a global storage area for Session-wide values. That is, you can create a Session variable that retains its value throughout page navigations and is accessible by all pages at a Web site for as long as a Session is active. A Session variable is created in the same way as a ViewState variable, by assigning a value to a name.

Session("name") = "value"
Figure 2-43. General format for creating a Session variable.

A Session variable works just like a ViewState variable except that it retains its value across all pages of a Web site, not just on post-back of the same page. For most Web page applications, ViewState variables are sufficient to maintain script values since variables tend to be localized within same-page scripts. Page postings normally occur to the same page. Session variables can be created, though, when the need arises to maintain data values across different pages.

The following example is a case in point. The page appearing in this frame presents a login form to gain access to a second page. The second page reports the login name entered on the first page. Code for this pair of pages is shown below.

Figure 2-44. Using a Session variable.

<SCRIPT Runat="Server">

Sub Get_Name (Src As Object, Args As EventArgs)

  If NameIn.Text <> "" Then
    Session("LoginName") = NameIn.Text
    Response.Redirect("SecondPage.aspx")
  End If

End Sub
</SCRIPT>

<form Runat="Server">

<h2>First Page</h2>

Please enter your name: 
<asp:TextBox id="NameIn" Runat="Server"/>
<asp:Button Text="Submit" Runat="Server"
  OnClick="Get_Name"/>
	
</form>
Listing 2-48. Code FirstPage.aspx.

<SCRIPT Runat="Server">

Sub Page_Load

  NameOut.Text = Session("LoginName")

End Sub
</SCRIPT>

<form Runat="Server">

<h2>Second Page</h2>

Welcome, 
<asp:Label id="NameOut" Runat="Server"/>,
to this Web site.

<p><a href="FirstPage.aspx">Return</a></p>

</form>
Listing 2-49. Code for SecondPage.aspx.

When a name is entered on FirstPage.aspx, it is assigned to Session variable Session("LoginName"). The name needs to be stored in a global Session variable because its value is needed on the second page. After redirection is made to SecondPage.aspx, its Page_Load script retrieves this Session variable and assigns it to an output Label as part of the welcome message. (The Response.Redirect method is a way to navigate to a different page from a script. It is discussed in more detail later.)

In this example a ViewState variable cannot be used. It retains its value only on post-back to the same page; here, there is no post-back operation. A URL is issued to navigate to a different page. Therefore, no ViewState variable is accessible by the second page; a Session variable must be used.

The Stateless Web

One of the difficulties of Web programming is that it takes place in a stateless environment. That is, information that is generated on one page is not normally accessible by any other page. When navigating from one page to the next, all knowledge of the previous page is lost. Neither the browser nor the server retains any information associated with the previous page. Each page access occurs as if it were the first time it has ever happened. Since the processing "states" of Web pages are not normally maintained between page navigations, it introduces difficulties in coordinating processing work between pages; thus, the introduction in ASP.NET of View States, Sessions, and other methods to "maintain state" between pages.

This problem of sharing information between pages of a Web site has been attacked in several ways in the past. Browser cookies are one way of saving information from one page to make it accessible by another page. Small amounts of data can be written from a page to a cookie file on the local computer and then retrieved from that same computer by a different page. However, cookies are limited in the amount of information that can be saved to the browser for later retrieval.

Also, appending query strings to URL addresses can be used to pass information between pages. You probably have noticed these query strings in your meanderings around the Web, although you might not have known what they were for. Below, for example, is a URL with a query string attached that was encountered while browsing the Web. A query string is the string of characters following a question mark (?) appended to a URL. It passes along information needed by the destination page to accomplish its processing.

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/xmlsdk/html/xm
mscXML.asp
Listing 2-50. Passing a query string to a page.

You will have occasion to use query strings for certain ASP.NET applications. Still, there are practical limits on how much information can be contained in a query string for passing between pages.

In a similar way, Web forms have been used to collect information on one page to pass along to a second page, often through "hidden" fields of data and control information. Although form submission is the primary technique for gathering user data for processing, it involves extra, and often times unnecessary, coding overhead to simply maintain state between pages.

Another method of maintaining the state of information between pages is to write it to files and databases. One page can create a data store on the server for retrieval and use by any other page. Although databases play a large and significant role in ASP.NET processing, they are not very practical for maintaining the usually simple states of affairs needed for Web pages to communicate with one another. They may be over-kill for the need to pass a few data values from one page to the next.

Because of the various limitations of browser cookies, query strings, form submissions, and databases for maintaining state between pages, the ASP.NET environment introduces ViewState and Session variables. Data values, whether they be user data or processing control information, can be assigned to these variables for carry forward between pages, maintaining state in a stateless environment. They are primary techniques to integrate processing among individual, independent, isolated, and really stupid Web pages with no memory of things past.