Programming Microsoft ASP.NET 4 - Dino Esposito [279]
// NB: for this code to work, GaugeBarDataItem must be
// a serializable type
public virtual GaugeBarDataItem DataItem
{
get
{
var o = ViewState["DataItem"] as GaugeBarDataItem;
return o ?? new GaugeBarDataItem();
}
set { ViewState["DataItem"] = value; }
}
Actually, nothing is “wrong” with the code per-se—but consider for a moment view-state size and performance. Saving a class type directly in the ViewState container results in the object being serialized using the binary formatter. The BinaryFormatter class—the standard way to serialize managed objects in .NET applications—is not particularly fast and is designed to save the entire state of the object, including both public and private members, both simple and complex. The use of the BinaryFormatter increases the response time for each request and generates a larger view-state output. By customizing the view-state serialization, you obtain much faster code and save exactly the information you need to save.
As a rule of thumb, you should use the ViewState container to store property values if the type of the property is primitive—a string, numbers, Boolean values, colors, dates, bytes, and arrays of any of these types. Reference types (for example, custom classes) should be serialized by implementing IStateManager and exposing the property via a get accessor like the one shown previously. As far as control development is concerned, this is commonly required for styles and data item properties.
Ad Hoc View-State Management
A control that has properties that take advantage of custom view-state serialization must override the SaveViewState and LoadViewState protected methods. These methods are defined on the Control class, and they indicate how to save and restore the state of the control to and from the view state. The default implementation of both methods takes care of the contents of only the ViewState container object.
protected override object SaveViewState()
{
// Get the standard state object-ViewState container
var baseState = base.SaveViewState();
// Get the state object for the DataItem property
var itemState = DataItem.SaveViewState();
// Get the state object for the TextStyle object
var styleState = TextStyle.SaveViewState();
// Pack everything into a unique object
return new Triplet(baseState, itemState, styleState);
}
The SaveViewState method of the GaugeBar control needs to save three objects: the standard view state, the DataItem property, and the TextStyle property. You get the standard view-state output by calling SaveViewState on the base class, and you get other state objects by calling SaveViewState on the IStateManager implementation of DataItem and TextStyle. The SaveViewState method on the control needs to return a single object, so you just group all data to return in a single object—typically, an array or a combination of Pair and Triplet objects.
The object returned by SaveViewState is received by LoadViewState, which extracts and assigns data back to the original objects.
protected override void LoadViewState(object savedState)
{
if (savedState != null)
{
var t = (Triplet) savedState;
base.LoadViewState(t.First);
DataItem.LoadViewState(t.Second);
TextStyle.LoadViewState(t.Third);
}
else
{
base.LoadViewState(null);
}
}
The IStateManager implementation of LoadViewState on the serialized objects determines how each object (for example, styles and data items) restores its own data.
Note that when DataItem.LoadViewState is called, the get accessor of DataItem is invoked and initializes the internal _dataItem member on the first call.
Getting Bound Data
In ASP.NET, a bound control obtains bound data through the PerformDataBinding method. Overriding this method is mandatory for any data-bound control because the standard implementation of the method does nothing. It is important to recall that