Introducing the State Bag

All server controls have a property called ViewState. This is defined in the Control class as the type StateBag, and allows server controls to store and retrieve values that are automatically round-tripped and recreated during a postback.

During the save state stage of a page, the ASP.NET framework enumerates all server controls within a page and persists their combined state into a hidden field called_VIEWSTATE. If you view any rendered ASP.NET containing a form element you will see this field:

<input type="hidden" name="_VIEWSTATE" value="dDwtMTcxOTc0MTI5NDs7Pg==" />

When a postback occurs, ASP.NET decodes the_VIEWSTATE hidden field and automatically repopulates the viewstate for each server control as they are created. This reloading of state occurs during the load state stage of a page for controls that are declared on an ASP.NET page. If a control is dynamically created, either on a page or within another composite control, the state will be loaded at the point of creation. ASP.NET keeps track of what viewstate hasn't been processed, and when a new control is added to the Controls property of a Control (remember a page is a control), it checks to see if it has any viewstate for the control. If it has, it is loaded into the control at that point.

To see viewstate in action, we will change our textbox control to store its current value in viewstate, rather than the _value field. By doing this, when LoadPostData is called to enable our textbox control to retrieve its new value, we can compare it to the old value held in viewstate. If the values are different we will return true, causing a TextChanged event to be raised in RaisePostDataChangedEvent. If the values are the same, we will return false so RaisePostDataChangedEvent is not called, and no event is raised.

The StateBag class implements the IDictionary interface, and for the most part is used just like the Hashtable class with a string key. All items stored are of the type System.Object, so any type can be held in the viewstate, and casting is required to retrieving an item.

In our earlier textbox control we used a string member variable _value to hold the current value of our textbox. We'll delete that variable and rewrite the property to use viewstate:

public string Text {

return String.Empty;

return (string) ViewState["value"];

ViewState["value"] = value;

Since we have deleted the _value member variable and replaced it with this property, we need to change all references to it, with the Text property. We could directly reference the ViewState where we previously used _value, but it's good practice to use properties to encapsulate our usage of viewstate, making our code cleaner and more maintainable. (For example, if we changed the viewstate key name used for the text value, we'd only have to do it in one place.)

With this new property in place, we can revise the LoadPostData to perform the check against the existing value as discussed:

bool IPostBackDataHandler.LoadPostData(string postDataKey, NameValueCollection postCollection) {

bool raiseEvent = false;

if ( Text != postCollection[postDataKey] )

raiseEvent = true; Text = postCollection[postDataKey]; return raiseEvent;

Before testing this code to prove that our TextChanged event is now only raised when the text changes, we need to make a small change to our ASP.NET page. As you'll recall from earlier, we have an event handler that sets the contents of a label to reflect our textbox value when our event is raised:

<script runat="server" language="C#">

private void OnNameChanged( object sender, EventArgs e ) {

status.Text = "Value changed to " + name.Text;

The label control uses viewstate to remember its value. When a postback occurs, even if this event is not raised, the label will still display the text from the previous postback, making it look like an event was raised. So to know if an event really was raised, we need to reset the value of the label during each postback. We could do this within the page init or load events, but since the label uses viewstate to retain its value, we can simply disable viewstate for the control using the EnableViewState attribute as follows:

<ASP:Label runat="server" EnableViewState="false" id="status" />

During the save state stage of a page, the ASP.NET page framework will not persist viewstate for the controls with an EnableViewState property of false. This change to the page will therefore make our label forget its value during each postback.

Setting EnableViewState to false does not prevent a control from remembering state using postback. As such, should you need to reset the value of a textbox, you'd have to clear the Text property in a page's init/load event.

With all these changes made, if we enter a value of "Wrox Press" and press the postback button, we will see that during the first postback our event is fired, and our label control displays the value:

If we click the postback button again, the textbox control will use its viewstate to determine that the postback value has not changed, and it will not fire its TextChanged event. Since the label control does not remember its state, as we disabled viewstate for it, the value-changed message will not appear during the second postback since the label will default back to its original blank value:

Our textbox control is now pretty functional for a simple control: it can remember its value during postback, can raise events when its text changes, and can have style properties applied in the same way as other web controls using the various style attributes:

<Wrox:MyTextBox id="name" runat="server"

BackColor="Green"

ForeColor="Yellow"

BorderColor="Red"

OnTextChanged="OnNameChanged" />

0 0

Post a comment