ViewState in ASP.NET 2.0
One of the most
interesting features of ASP.NET 2.0 is the open way it allows the programmer to
manipulate a control's viewstate. The viewstate should be the first choice to
save data to access across postbacks. If the information is to be shared across
different controls, it can be placed in the viewstate of the page, if it is to
be accessed within the scope of a single control, then it can be placed within
the viewstate of the control. The type of info one can save here may be simple
data types or even complex user defined objects!
The ASP.NET Page Life Cycle
In order to understand how
to use the viewstate properly, it is necessary to understand the ASP.NET page
lifecycle. Whenever a page is requested from a server, it passes through the
following steps:
1. The Page
object is created. It contains all the same values as defined in the .aspx
files. All objects/user controls placed within it are created one by one. For
example, if we have a textbox on the .aspx file,
<asp:TextBox
runat="server"
id="tb1">TheValue</asp:TextBox>
After this
stage, this textbox will have the value "TheValue"inside it.
2. The state of the page is restored based on the viewstate. If
on the client side, the value of our textbox has been changed, it will now have
the new value. (Obviously, this stage does not happen when the page is requested
for the first time)
3. The Page_Load event is fired, at this point, the programmer
can write the custom code.
4. The appropriate event handler (if any) is fired.
Storing Simple Data Types in the ViewState
Like most types of state
management in ASP.NET, view state relies on a dictionary collection, where each
item is indexed with a unique string name. For example, consider this code:
ViewState["ViewStateVariableName"] = 1;
This places the value 1 (or
rather, an integer that contains the value 1) into the ViewState collection and
gives it the descriptive name ViewStateVariable. If there is currently no item
with the name ViewStateVariable, a new item will be added automatically. If
there is already an item indexed under this name, it will be replaced.
You can access this variable
anywhere within the page/control where the viewstate variable has been added.
When retrieving a value, you use the key name.
int
number = (int) ViewState["ViewStateVariable"];
You also need to cast the
retrieved value to the appropriate data type. This is because the ViewState
collection stores all items as generic objects which also give you the
flexibility to store any type of basic data types in it. In fact, you can even
store your custom objects in the view state.
Storing Objects in View State
You can store your own
objects in view state just as easily as you store numeric and string types.
However, to store an item in view state, ASP.NET must be able to convert it into
a stream of bytes so that it can be added to the hidden input field in the page.
This process is called serialization. If your objects aren't serializable (and
by default they aren't), you'll receive an error message when you attempt to
place them in view state.
To make your objects
serializable, all you need to do is to add the 'Serializable' attribute before
your class declaration. For example, here's an exceedingly simple Student class:
[Serializable]
public
class Student
{
public string
firstName;
public string
lastName;
public Student(string
fName, string lName)
{
firstName =
fName;
lastName =
lName;
}
}
Because the Student class is
marked as serializable, it can be stored in view state:
//
Storing a student in view state.
Student stud =
new Student("John",
"Doe");
ViewState["CurrentStudent"] = stud;
Remember, when using custom
objects, you'll need to cast your data when you retrieve it from view state.
//
Retrieve a student from view state.
Student stud = (Student) ViewState["CurrentCustomer"];
To be
serializable, your classes you must meet these requirements:
• Your class must have the
Serializable attribute.
• Any classes it derives from
must have the Serializable attribute.
• All the private variables
of the class must be serializable data types. Any nonserializable data type must
be decorated with the NonSerialized attribute (which means it is simply ignored
during the serialization process).
Example 1: Making a PostBackCounter
Let's say you want to keep
track of the number of times a page is posted back to the server. This info can
be kept inside the session, or even inside a hidden field control in the page.
However, keeping it in the session forces you to clear up or reset this variable
every time the user moves to a new page. Keeping it in a hidden filed or an
invisible text box on the page might clutter your .aspx file with controls that
can be changed by malicious users. Using the view state in this scenario is an
elegant way to persist info across postbacks that will be accessible only to the
programmer and the code.
Suppose our .aspx page
control contains one button and one label inside the form.
<asp:Label
id="Label1"
runat="server"/>
<asp:Button
id="Button1"
runat="server"
OnClick="Button1_Click"/>
On the code behind file we
have the page load event and the event handler for the click event of button1.
protected
void Page_Load(object
sender, System.EventArgs e)
{
//First we check if the page is loaded for the first
time,
if (!IsPostBack)
{
ViewState["PostBackCounter"]
= 0;
}
//Now, we assign the variable value in the text box
int counter = (int)ViewState["PostBackCounter"];
Label1.Text =
counter.ToString();
}
Now, the label will always
display the post back count!
Let's say we increment the
post back count only when our button is clicked. In this case, we need to write
some code on the button click event handler.
protected
void Button1_Click(object
sender, System.EventArgs e)
{
int oldCount = (int)ViewState["PostBackCounter"];
int newCount = oldCount + 1;
//First, we assign this new value to the label
Label1.Text =
newCount.ToString();
//Secondly, we replace the old value of the view state
so that the new value is read the next //time
ViewState["PostBackCounter"]
= newCount;
}
Now, every time the user will
click on this button, the text will show an incremented number!
Example 2: Tracking Dynamic Controls
One of the main challenges
with working with dynamically added controls is that these controls must be
programmatically added on each postback. That is, you can't just load these
controls on the first page load, and then not reload them on subsequent
postbacks. Failure to explicitly add the controls on each postback will cause
the controls to literally disappear on postbacks.
Let's build on our previous
example. Only this time, instead of an integer, we'll keep a List<string> in the
view state. Instead of one label, we are going to dynamically add a new Label
every time the button is clicked. And, the value of the new label will be given
by the user inside a text box.
So, out .aspx page will look
like this:
<asp:TextBox
id="TextBox1"
runat="server"/>
<asp:Button
id="Button1"
runat="server"
OnClick="Button1_Click"/>
<asp:PlaceHolder
id="PlaceHolder1"
runat="server"
/>
The placeholder will act as
the container on which we shall load out Labels.
On the code behind cs file,
we have page load event, which
initializes the controls:
protected
void Page_Load(object
sender, System.EventArgs e)
{
//First we check if the page is loaded for the first
time,
if(!IsPostBack)
{
List<string>
temp = new List<string>();
ViewState["allTextData"]
= temp;
}
List<string>
myTextData = (List<string>) ViewState["allTextData"];
//Loading the dynamic controls
foreach(string
data in myTextData)
{
AddDynamicLabel
(data);
}
}
The AddDynamicLabel()
function dynamically adds a label in the placeholder.
private
void AddDynamicLabel (string
data)
{
//Making the new label and giving it the appropriate
value
Label l = new
Label();
l.Text = data +
"<br/>";
//Adding the label into the page
PlaceHolder1.Controls.Add(l);
}
And finally we have the
Button1 click event handler, where we add a brand new label in our placeholder.
protected void Button1_Click(object sender, System.EventArgs e)
{
//Adding the new text as a label inside the placeholder
AddDynamicLabel (TextBox1.Text);
//Adding the new text inside the viewstate List, for retreival on subsequent postbacks
List myTextData = (List) ViewState["allTextData"];
myTextData.Add(TextBox1.Text);
ViewState["allTextData"] = myTextData;
}
When to use the ViewState
View state is a good way to
store data in between postbacks, because it doesn't take up any memory on the
server side and it doesn't impose any arbitrary usage limits (such as a
timeout). So, what might force you to abandon view state for another type of
state management? Here are three possible reasons:
• You need to store
mission-critical data that the user cannot be allowed to tamper with. (An
ingenious user could modify the view state information in a postback request.)
In this case, consider session state.
• You need to store
information that will be used by multiple pages. In this case, consider session
state, cookies, or the query string.
• You need to store an
extremely large amount of information, and you don't want to slow down page
transmission times. In this case, consider using a database, or possibly session
state.
Remember, any data that
you put in the view state will be part of the data stream that goes to the
client and then comes back to the server, so be very careful while putting large
chunks of data in the ViewState! |