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 outsurvey 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.
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.
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.