How the PreserveProperty Control Works
The implementation of the control is pretty simple. It has a
PreservedProperties collection that holds the
ControlID (or instance if available) and the property name. When you call the
PreserveProperty() method on the control or declaratively define the control on a page, each
ControlId and
Property name is stored in the collection. The collection is a
Generic List. I love generics for this stuffno more creating custom collections for child controls!
To get the collection to work as a designable collection you have to use a few custom attributes on the Control class as well as the collection property. You also need to implement
AddParsedSubObject (or use the
DefaultProperty attribute if you only have a single collection) to get the child control values to be added as PreservedProperty objects from the declarative script definition.
Listing 1 shows the class and collection definition.
The code deals with storing the
PreservedProperties in a simple strongly-typed List collection. The control uses a custom class to hold the control's ID, an instance reference if available, and the ID for the control. The
PreservedProperties collection is a temporary holding container for the property identification; the values are not actually stored in it until later in the request cycle.
Encoding and Decoding the Persisted Data
The actual hooks for encoding and decoding depend on the
StorageMode which includes
ControlState,
HiddenVariable,
SessionVariable, and
CachePerPage. All but
ControlState require explicit hooks to fire for the encoding and decoding to occur. In these modes, hook calls are made in
OnInit() and
OnPreRender() of the control.
OnInit() fires very early in the page cycle so that the preserved value can be retrieved prior to any other state retrieval. Specifically, the property value is set before ViewState assigns its value or before a postback value is assigned. This makes preserved properties have the lowest priority in value assignment, which is the intended behavior.
The PreserveProperty control lets you work with properties and fields without having to worry about the persistence mechanism.
|
|
When the value is stored it's done in the
As part of the
PreservePropertyControl.OnPreRender().
ControlState Persistance in ASP.NET 2.0
ControlState is a new feature of ASP.NET 2.0. It's meant as a control internal state implementation used to store essential data between page postbacks. It uses ViewState for storage, but unlike stock ViewState it is not affected by the setting of EnableViewState and always writes out its values.
You can easily work with ControlState by implementing two methods on the Page class:
SaveControlState and
LoadControlState. These methods work by returning and retrieving an object value that ASP.NET persists internally. These objects must either be simple types, serializable, or implement a TypeConverter. As with ViewState, the rules are determined through the .NET LosFormatter class, which is an optimized serializer ASP.NET uses for encoding ViewState strings. You can find ControlState in the System.Web.UI namespace.
To enable ControlState you have to tell the page to use it, for example:
this.Page.RegisterRequiresControlState(this);
You'd typically call this in the
OnInit() method of a control.
You then override
SaveControlState() to return an object that contains the state. If you have more than one value to persist, a typical object is a Hashtable with all the persisted values in it. When the page posts back,
LoadControlState is called with an object parameter that essentially restores that object saved in
SaveControlState. It's a very easy mechanism that deals with all the encoding details. All you do is return an object and off you go.
Listing 2 shows the
SaveControlState() and
LoadControlState() methods for managing ControlState.
SaveControlState() walks through its PreservedProperties collection and starts by finding each control instance. If you used code to add properties, then you likely passed in a control instance reference, but when using declarative script a string reference is stored, and you'll need to use
FindControl to actually locate the control. Once a control reference is available the value for the property is retrieved via Reflection. Note the flags to retrieve both fields and properties and both public and non-public members. Also note that you cannot preserve private members on a Page object.
The next step is to add the value to a Hashtable with the key being a concatenation of the unique
ControlID and the property name. Hashtables are efficient at storing key value pairs and persist in a lightweight fashion. The
SaveControlState method then returns this Hashtable for persistence.
Loading the data on a postback works this process in reverse.
LoadControlState() receives the Hashtable as an input and then runs through the collection, retrieving each key and value. The code in
LoadControlState() splits the key back into its
ControlID and property name components and
FindControl is then used to get an instance, followed by Reflection to set the control's value.
This is a very straightforward process and using ControlState takes very little code to implement this solution.