Lesson 3: Maintaining State Information
In this lesson, you ll learn how to retain data on a Web form between requests. ASP.NET provides different mechanisms for saving form data, depending on the nature and scope of the information.
After this lesson, you will be able to
Choose from the different ways to save data on a Web form based on your needs
Transfer data between Web forms with query strings
Save items on the client as cookies
Store page data in a Web form s ViewState property
Keep application-wide variables in Application and Session state
Structure access to Application and Session state variables to avoid programming errors
Estimated lesson time: 30 minutes
Overview of Levels of State
As explained in Chapter 2, Creating Web Forms Applications, Web forms are created and destroyed each time a client browser makes a request. Because of this characteristic, variables declared within a Web form do not retain their value after a page is displayed. To solve this problem, ASP.NET provides the following ways to retain variables between requests:
Use this object to retrieve public members of one Web form s class from a subsequently displayed Web form.
Use these strings to pass information between requests and responses as part of the Web address. Query strings are visible to the user, so they should not contain secure information such as passwords.
Use cookies to store small amounts of information on a client. Clients might refuse cookies, so your code has to anticipate that possibility.
ASP.NET stores items added to a page s ViewState property as hidden fields on the page.
Use Session state variables to store items that you want keep local to the current session (single user).
Use Application state variables to store items that you want be available to all users of the application.
Using Context.Handler
Ordinarily the members of one Web form are unavailable from a subsequently displayed Web form. However, when navigating between Web forms using the Transfer or Execute method, you can retrieve read-only properties from the previous Web form the first time a subsequent Web form is displayed.
For example, the following code declares a public property named Value that returns the data entered in a text box on the Context1 Web form:
Visual Basic .NET
Public Class Context1 Inherits System.Web.UI.Page ' Create a property to return the value of a text box. Friend ReadOnly Property Value() As String Get Return txtValue.Text End Get End Property Private Sub butNext_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles butNext.Click ' Transfer to the next page. Server.Transfer("Context2.aspx") End Sub End Class
Visual C#
public class Context1 : System.Web.UI.Page { // Create a property to return the value of a text box. internal string Value { get { return txtValue.Text; } } private void butNext_Click(object sender, System.EventArgs e) { // Transfer to the next page. Server.Transfer("Context2.aspx"); } }
When the user clicks the Button control, he or she is transferred to the Context2 Web form, which retrieves the previous Web form s Context.Handler object, coerces that object to the previous Web form s type, and then accesses the Context1 Value property, as shown here:
Visual Basic
Public Class Context2 Inherits System.Web.UI.Page Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load ' The first time this page is displayed If Not IsPostBack Then ' Declare an object with the previous page's type. Dim LastPage As Context1 ' Convert the context handler to the previous page's type. LastPage = CType(Context.Handler, Context1) ' Display the value from the page's Value property. lblValue.Text = LastPage.Value End If End Sub End Class
Visual C#
public class Context2 : System.Web.UI.Page { private void Page_Load(object sender, System.EventArgs e) { if (!IsPostBack) { // Declare an object using the previous page's type. Context1 LastPage; // Coerce the Context.Handler object to the page's type. LastPage = (Context1)Context.Handler; // Display the Value property. lblValue.Text = LastPage.Value; } } private void butBack_Click(object sender, System.EventArgs e) { // Transfer back to the preceding page. Server.Transfer("Context1.aspx"); } }
You can transfer complex types from one Web form to another in this way, but remember that the Context.Handler object reflects only the immediately previous Web form on postback events, it will reflect the currently displayed Web form. Also remember that the Transfer method does not notify the user s browser that the page address has changed. If the user clicks Refresh on his or her browser, the user receives a warning that the page can t be refreshed without resending information.
Using Query Strings
Query strings allow you to send additional information along with an address. In HTML, query strings appear after a question mark in a hyperlink, as shown here:
<A HREF= "WebForm1.aspx?UName=Wombat">Query string sample.</ A>
To send a query string in code, add it to the address of a Redirect method. The following Click event procedure is equivalent to the preceding HTML:
Visual Basic .NET
Private Sub butSend_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles Button1.Click ' Redisplay this page with a QueryString. Response.Redirect(Request.FilePath & "?UName=Wombat") End Sub
Visual C#
private void butSend_Click(object sender, System.EventArgs e) { // Redisplay this page with a QueryString Response.Redirect(Request.FilePath + "?UName=Wombat"); }
To retrieve a query string in code, use the QueryString method of the Request object. The following code displays the UName item from the query string created in the preceding examples:
Visual Basic .NET
Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load ' Check for QueryString If Not IsNothing(Request.QueryString("UName")) Then ' Display message. lblQstring.Text = "QueryString UName=" ' Display QueryString lblQstring.Text += Request.QueryString("UName") End If End Sub
Visual C#
private void Page_Load(object sender, System.EventArgs e) { // Check for QueryString if (Request.QueryString["UName"] != null) { // Display message. lblQstring.Text = "QueryString UName="; // Display QueryString lblQstring.Text += Request.QueryString["UName"]; } }
Using Cookies
Use cookies to store small amounts of information on the client s machine. Web sites often use cookies to store user preferences or other information that is client-specific. Because cookies can be refused, it is important to check whether the browser allows them before you try to create them.
The following code checks whether a browser allows cookies and then saves user preferences if it does:
Visual Basic .NET
Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load ' Run the first time this page is displayed. If Not IsPostBack Then ' If the browser supports cookies If Request.Browser.Cookies Then ' Create a cookie. Dim cookUPrefs As New HttpCookie("UPrefs") cookUPrefs.Value = "English" ' Add the cookie. Response.Cookies.Add(cookUPrefs) End If End If End Sub
Visual C#
private void Page_Load(object sender, System.EventArgs e) { // Run the first time this page is displayed. if(!IsPostBack) // If the browser supports cookies. if(Request.Browser.Cookies) { // Create a cookie. HttpCookie cookUPrefs = new HttpCookie("UPrefs"); cookUPrefs.Value = "English"; // Add the cookie. Response.Cookies.Add(cookUPrefs); } }
The following code checks for a cookie and then gets the cookie if it is available:
Visual Basic .NET
Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load ' Run first time page is displayed. If Not IsPostBack Then ' Check if Browser supports cookies. If Request.Browser.Cookies Then ' Check if the UPrefs cookie exists. If Not IsNothing(Request.Cookies("UPrefs")) Then ' Save the value of the cookie. Session("Lang") = Request.Cookies("UPrefs").Value End If End If End If End Sub
Visual C#
private void Page_Load(object sender, System.EventArgs e) { // Run the first time this page is displayed. if(!IsPostBack) // If the browser supports cookies. if(Request.Browser.Cookies) // Check if the UPrefs cookie exists if(Request.Cookies["UPrefs"] != null) // Save the value of the cookie. Session["Lang"] = Request.Cookies["UPrefs"]. Value; }
Using ViewState
Use the ViewState property to save data in a hidden field on a page. Because ViewState stores data on the page, it is limited to items that can be serialized. If you want to store more complex items in ViewState, you must convert the items to and from a string.
For example, the following code adds text from a text box to cells in a table on the page. Because you can t store objects directly in ViewState, you must store the strings in the butAdd_Click procedure and then create the table row controls from the strings, as shown in the Page_PreRender procedure.
Visual Basic .NET
Private Sub butAdd_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles butAdd.Click ' Add text to ViewState ViewState.Add(ViewState.Count, txtValue.Text) End Sub ' This executes just before the page is displayed, ' so it exhibits the affects of the button clicks. Private Sub Page_Prerender(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.PreRender ' Create a new row object. Dim rowNew As New TableRow ' Add row to the table. tblViewState.Rows.Add(rowNew) ' Variable used to enumerate ViewState collection. Dim Item As StateItem ' For each item in the ViewState For Each Item In ViewState.Values() ' Create a new table cell Dim celNew As New TableCell ' Set cell text. celNew.Text = Item.Value ' Add cell to row. rowNew.Cells.Add(celNew) ' Create a new row every four cells. If rowNew.Cells.Count > 3 Then rowNew = New TableRow tblViewState.Rows.Add(rowNew) End If Next End Sub
Visual C#
// This executes just before the page is displayed, // so it exhibits the affects of the button clicks. private void Page_PreRender(object sender, EventArgs e) { // Create a new row object. TableRow rowNew = new TableRow(); // Add row to the table tblViewState.Rows.Add(rowNew); // For each item in the ViewState foreach (StateItem Item in ViewState.Values) { // Create a new table cell TableCell celNew = new TableCell(); // Set cell text. celNew.Text = Item.Value.ToString(); // Add cell to row. rowNew.Cells.Add(celNew); // Create a new row every four cells. if (rowNew.Cells.Count > 3) { rowNew = new TableRow(); tblViewState.Rows.Add(rowNew); } } }
ASP.NET hashes the hidden data stored on the page so that it is not intelligible to users. If you add a few items to the table using the preceding code and then choose View Source from the browser, the HTML for the hidden field looks something like this:
</HEAD> <body MS_POSITIONING="GridLayout"> <form name="Form1" method="post" action="WebForm1.aspx" > <input type="hidden" name="__VIEWSTATE" value="dDwtMTMwNzIzM zU0Mzt0PHA8bDwwOzE7MjszOzQ7PjtsPFxlO1RoaXMgaXMgc29tZSB0ZXh 0O1RoaXMgaXMgc29tZSB0ZXh0O1RoaXMgaXMgc29tZSB0ZXh0O1RoaXMgaXM gc29tZSB0ZXh0Oz4+Ozs+Oz4=" />
This hashing prevents malicious users from manipulating ViewState data between requests to corrupt data on the server and also makes the ViewState information from one Web form unreadable from other Web forms.
Using Application and Session States
Use the Application and Session states to store data that you want to keep for the lifetime of an application or for the lifetime of a session. You can store any type of data in the Application or Session state, including objects. However, you should consider the following issues before using the Application and Session states:
Application and Session state variables are created on the fly, without variable name or type checking. You should limit your access points to these variables.
Maintaining Session state affects performance. Session state can be turned off at the application and page levels.
Application state variables are available throughout the current process, but not across processes. If an application is scaled to run on multiple servers or on multiple processors within a server, each process has its own Application state.
The Web application s boundaries determine the scope of the Application state.
Structuring Access to State Variables
Application and Session state variables are powerful and therefore scary things. It is easy to introduce some old-style Basic errors in your code if you use these variables in an unstructured manner. For instance, the following code references two different Application state variables:
Visual Basic .NET
Application("Uname") = "Wombat" Response.Write(Application("Unamme"))
Visual C#
Application["Uname"] = "Wombat"; Response.Write(Application["Unamme"]);
The first line creates a variable and stores some text in it. The second line retrieves a new, empty variable and displays nothing, simply because the variable name is misspelled. The problem is obvious here, but if it occurred deep in a decision structure, it would be very hard to find.
To ameliorate this problem, structure your access to Application and Session state variables. The easiest way to do this is to declare a page-level variable for each item you need, retrieve the Application or Session state value in the Page_Load event procedure, and save the page-level variables back to state in the Page_Unload event procedure. You might want to make retrieving and saving state variables from a consistent location part of your coding conventions.
The following code demonstrates structuring access to state variables:
Visual Basic .NET
Public Class AppSessState Inherits System.Web.UI.Page ' Variable for controlling state access Dim strAppState, strSessState As String Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load ' Get Application and session variables here. ' Check if they exist. If Not IsNothing(Application("AppState")) Then _ strAppState = Application("AppState") If Not IsNothing(Session("SessState")) Then _ strSessState = Session("SessState") End Sub Private Sub butApp_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles butApp.Click strAppState = txtValue.Text End Sub Private Sub butSess_Click(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles butSess.Click strSessState = txtValue.Text End Sub Private Sub Page_PreRender(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles MyBase.PreRender ' Display values. lblValue.Text = "Application state is: " lblValue.Text += strAppState lblValue.Text += " Session state is: " lblValue.Text += strSessState End Sub Private Sub Page_Unload(ByVal sender As Object, _ ByVal e As System.EventArgs) Handles MyBase.Unload ' Store values before unloading page. Application("AppState") = strAppState Session("SessState") = strSessState End Sub End Class
Visual C#
public class AppSessState : System.Web.UI.Page { // Variable for controlling state access string strAppState = "", strSessState = ""; private void Page_Load(object sender, System.EventArgs e) { // Get Application and session variables here. // Check if they exist. if (Application["AppState"] != null) strAppState = Application["AppState"].ToString(); if (Session["SessState"] != null) strSessState = Session["SessState"].ToString(); } private void butApp_Click(object sender, System.EventArgs e) { strAppState = txtValue.Text; } private void butSess_Click(object sender, System.EventArgs e) { strSessState = txtValue.Text; } private void AppSessState_PreRender(object sender, EventArgs e) { // Display values. lblValue.Text = "Application state is: "; lblValue.Text += strAppState; lblValue.Text += " Session state is: "; lblValue.Text += strSessState; } private void AppSessState_Unload(object sender, EventArgs e) { // Store values before unloading page. Application["AppState"] = strAppState; Session["SessState"] = strSessState; } }
IMPORTANT
In Visual C#, be sure to test whether a state variable is null before invoking any of its methods, such as ToString. Otherwise, you will receive a run-time error if the state variable does not contain a value.
Turning Off Session State
ASP.NET maintains Session state for each page in your application by default. If a page does not require state information at the session level, you can turn this off to achieve better performance.
To turn Session state off for a Web form:
From the Web Form Properties window, set EnableSessionState to False.
To turn Session state off for an entire application:
In the Web.config file, set the <sessionstate mode=> tag to False, as shown here:
<sessionstate mode="False"/>