Inside ASP.NET Control Properties

Design and Implement Control Properties

Dino Esposito

October 30, 2009

13 Min Read
ITPro Today logo

CoreCoder

LANGUAGES: C#| VB.NET

ASP.NETVERSIONS: 1.x | 2.0

 

Inside ASP.NET Control Properties

Design and Implement Control Properties

 

By Dino Esposito

 

There s a tricky question that s been coming up regularlyduring conference talks and classes in the past few years. It refers to afeature of ASP.NET that has not been touched in version 2.0: the implementationof control properties. Sooner or later, all developers get to write customASP.NET controls, and, when it comes to this, properties are one of the firstfeatures they must come to grips with. Most of the sample code available outthere through articles and books shows the same implementation of controlproperties based on viewstate. Many smart developers, new to controldevelopment, are therefore led to think that viewstate and control propertiesare an indissoluble and powerful duo.

 

As the developer makes progress on the subject, he or shesoon realizes that there s more to learn and figure out. Dissecting the sourcecode of more sophisticated examples, as well as using .NET Reflector to snoopinto the decompiled code of ASP.NET controls, one can see that there are atleast two ways of implementing the storage of control properties: viewstate andlocal variables.

 

You don t have to be an ASP.NET controls super-expert tonote that these two solutions seem to be diametrically opposed. Nevertheless,being adopted in the same ASP.NET framework, both solutions should be valid andeffective in their own scenarios.

 

In this article, I ll take the plunge into the internalsof ASP.NET control properties and discuss how control properties manage theirvalues in conjunction with the viewstate management API of ASP.NET pages.

 

The ABCs of Control Properties

A property is a public member of a class and basically representsa typed value associated with each instance of the class. The value of theproperty attribute can be read and written. The property sports special toolsto read and write its own value. These tools are usually referred to asaccessor (get) and mutator (set). Although the property is publicly exposed asan attribute, it is internally implemented through a pair of get/set methods.If either miss, the property is read-only orwrite-only. The property is simply a public name that top-level code uses toread and write a value stored elsewhere. A property is a public name used toabstract a value. Properties are not the only option you have to storeinformation in a class. In addition to properties, you can make use of fields.

 

Fields and properties are almost indistinguishable from aclient perspective, but are declared differently within a class. Whereas fieldsare simply public variables that a class makes visible to callers, propertiesuse ad hoc blocks of code (mutatorand accessor) to control how values are set or returned. Unlike properties,fields provide no abstraction. The field coincides with the value and exposesthe value directly to the caller code.

 

In C# you declare properties and fields using nearly thesame syntax:

 

// This is a field

public string Text;

// This is a property

public string Text

{

  get {

      // retrieve andreturn the value of the property

  }

  set {

      // set the new valuefor the property

  }

}

 

In Visual Basic .NET, properties require a special syntaxthat clearly distinguishes them from fields:

 

' This is a field

Public Text As String

' This is a property

Public Property Text As String

  Get

      ' retrieve andreturn the value of the property

  End Get

  Set (ByVal value AsString)

      ' set the new valuefor the property

  End Set

End Property

 

The get accessor of the property is used whenever the codereads or assigns the value of the property, as in the code below:

 

Response.Write(ctl.Text);

 

The property mutator is used whenever the code assigns anew value to the property:

 

ctl.Text = "Hello, world";

 

In this case, the set mutator is used and the assignedvalue is accessed through the value parameter in Visual Basic .NET and thevalue keyword in C#.

 

As far as control properties are concerned, the storage ofthe internal value is a key point. When you implement a property, you must knowalready where the property is going to store any value it is expected to returnor cache.

 

Choosing a Storage Medium

ASP.NET controls live in the context of ASP.NET pages.ASP.NET pages, in turn, are accessed through successive requests in a purelystateless fashion. Each request causes the ASP.NET runtime to create a newprocessing environment where any values related to any previous request,including control properties, are irreversibly lost.

 

So, should control properties be persistent? That s a goodquestion. The role of a property is that of letting developers set attributeson the control so that the control can render out appropriate markup. For thisto happen, properties don t have to be persistent. Period.

 

However, the ASP.NET platform makes a point ofimplementing automatic state maintenance through the viewstate. The viewstateis a container where all controls in the page can store their own key values tohave them back when the same page posts back. At the end of each request, anyvalues that a control has placed in the viewstate are persisted and restoredwhen the same page instance posts back after a user action. The ASP.NET runtimeserializes the contents of the viewstate to a hidden field when the request isgoing to end and deserializes it when the request comes back.

 

What s the overall advantage you get out of the viewstate?Simply put, a control property that uses the viewstate as its storage medium isautomatically persisted and retrieves its value across successive requests. Putanother way, using the viewstate for control properties makesthe control work statefully over a stateless protocol like HTTP. Persistentproperties are not a strict requirement to make ASP.NET controls work. However,having persistent properties greatly simplifies the development of Webapplications using controls. Built-in ASP.NET controls make extensive use ofthe viewstate to persist the value of their own properties. Using public fieldsis not considered a good practice. The code in Figure 1 shows the typicalviewstate-based implementation of a control property.

 

// Text string property

public string Text

{

  get {

      object o =ViewState["Text"];

      if (o == null)

         returnString.Empty;

      return (string) o;

  }

  set {

      ViewState["Text"] = value;

  }

}

' Text string property

Public Property Text As String

  Get

      Dim o As Object =ViewState("Text")

      If o Is Nothing Then

          Return String.Empty;

      End If

      Return DirectCast(o,String);

  End Get

  Set (ByVal value AsString)

      ViewState("Text") = value

  End Set

End Property

Figure 1: Typicalviewstate-based implementation of a control property.

 

By convention, each property takes a viewstate slot namedafter the property name. The get accessor first attempts toget a value out of the given viewstate entry. If this value is null thatis, the property has not been initialized the default value of the propertyis returned. If the property holds a value, that value is cast to the rightproperty type and returned. Note that giving properties their default value inthe control class constructor is not considered a good practice. If you do so,in fact, you end up having code that relates to a property in two distinctplaces: the property definition and the class constructor.

 

If Visual Basic .NET is your favorite programminglanguage, make sure you use DirectCast instead of CType to convert theviewstate object to the property type. CType succeeds as long as there is avalid conversion defined between the expression and the type. DirectCast,instead, requires that the run-time type of the object be the same as thespecified type. If this is the case, the performance of DirectCast is better.Put another way, either DirectCast fails and throws an exception or it runsfaster. In this particular scenario, you are the only one who can put a valueof the wrong type in the viewstate entry. It is safe to assume that the typesmatch. In addition, the use of DirectCast ensures that developers working withyour control get an exception if they store values of incompatible types in theproperty (assuming that the compiler doesn t catch this at compile time).

 

Using Other Storage Media

In case of (real) need, you can replace the viewstate withother ASP.NET intrinsic objects, such as Session or Cache. Note that usingSession or Cache is not a way to optimize the behavior of the application(quite the reverse, I d say), but should be done in case you want to expose asclassic control properties information that is already stored in Session orCache. Consider also that the viewstate object is scoped to the current page.This means that you can handle multiple instances of the same page and multipleinstances of the same control in the same page and each page and control willhave its own copy of the viewstate. No conflicts are possible betweenproperties as long as they belong to distinct controls. The same is notguaranteed if a property is persisted to Session or Cache. If Session is used,the scope is the session and you should guarantee that a property slot has aname that is unique to the page and the control. It s even worse if Cache isused; in this case, the scope is the entire application.

 

Properties of a Complex Type

A property persisted to the viewstate undergoes aserialization step. The system component responsible for viewstateserialization has been changed in ASP.NET 2.0. The LosFormatter class used byASP.NET 1.x has been replaced by the ObjectStateFormatter class in ASP.NET 2.0.The key factor differentiating these classes is the serialization algorithmemployed and ultimately the final size of the generated text. TheObjectStateFormatter class uses an improved algorithm and saves about 50percent of the viewstate size compared to ASP.NET 1.x.

 

Both classes operate a clear distinction between simpleand complex types. Simple types are strings, numbers, enumerations, dates,colors, booleans, bytes, Unit, arrays, and pairs or triplets of the simpletypes. Any classes are considered complex types.

 

For simple types, the viewstate formatter employs aspecific, optimized algorithm that minimizes the amount of information stored.For complex types, two options are considered. If a type converter exists forthe class, it is used to convert the contents of the class to a string.Otherwise, the BinaryFormatter is invoked to serialize the class instance to anarray of bytes. Using the BinaryFormatter class is a last resort and should beavoided whenever possible. The BinaryFormatter class can serialize any .NETclass that is flagged with the [Serializable] attribute, but is not the fastestoption and it generates a relatively large output. The point is thatBinaryFormatter is designed to pursue framework run-time object serializationand to persist and restore the whole state of an object, including assemblyinformation. To persist a control property, you don tneed more than a bunch of values. For this reason, a type converter ispreferable.

 

What is a type converter? A type converter is simply aclass that takes care of converting an object of a type to an object of anothertype, including a string type. It is nothing really different from a sort ofsimplified and made-to-measure text serializer. You create a type converter byderiving a class from TypeConverter and overriding a few methods: CanConvertFrom,CanConvertTo, ConvertFrom, and ConvertTo.

 

Imagine now that your control needs a property that is acollection of custom types. For example, list controls such as DropDownListfeature the Items property whose type is ListItemCollection. The signature ofthe collection class is shown here:

 

public sealed class ListItemCollection :

 IList, IStateManager

{

  :

}

Public NotInheritable Class ListItemCollection :

 Implements IList,IStateManager

  :

End Class

 

As you can see, the class is a collection and implementsthe IStateManager interface. Let s take a look at the implementation of theItems property in a typical list control such as the DropDownList (see Figure2).

 

private ListItemCollection items;

public virtual ListItemCollection Items

{

     get

     {

           if (items ==null)

           {

                 items =new ListItemCollection();

                 if (base.IsTrackingViewState)

                       items.TrackViewState();

           }

           return items;

     }

}

Private items As ListItemCollection

Public Overridable ReadOnly Property Items As ListItemCollection

     Get

            If (Me.items Is Nothing) Then

                 Me.items= New ListItemCollection

                 IfMyBase.IsTrackingViewState Then

                       Me.items.TrackViewState

                 End If

           End If

           Return Me.items

     End Get

End Property

Figure 2: Implementingthe Items property in a typical list control.

 

The property is read-only and is implemented through a local variable named items. At first glance youmay form two erroneous ideas that the property is not persisted to theviewstate and that the property value doesn t survive a page postback becausethe property is saved to a local variable.

 

In spite of appearances, the Items property is persistedto the viewstate. However, it is serialized through a custom mechanism anddoesn t pass through the ViewState container. Would writing the code shown inFigure 3 be a mistake?

 

public ListItemCollection Items

{

  get {

      object o =ViewState["Items"];

      if (o == null)

         return newListItemCollection;

      return(ListItemCollection) o;

  }

}

Public Property Items As ListItemCollection

  Get

      Dim o As Object =ViewState("Items")

      If o Is Nothing Then

         Return NewListItemCollection;

      End If

      Return DirectCast(o,ListItemCollection);

  End Get

End Property

Figure 3: Right orwrong?

 

The code in Figure 3 is correct and works just fine aslong as the ListItemCollection class is serializable. The point is, how efficient is this code? Not very, I d say, because theListItemCollection class (and any other custom class you use in similarsituations) will be serialized through the binary formatter. Should you use atype converter instead? It would be much better, but requires you to write,document, and maintain yet another class. The best practice consists ofembedding the viewstate serialization code in the control itself.

 

Serializing Custom Types

The methods of the IStateManager interface represent thecontract by means of which a class can be serialized to, and deserialized from,the viewstate. Figure 4 details the methods of the interface. By implementing IStateManager,a class determines how its contents should be saved and restored to and fromthe viewstate. A class that implements IStateManager doesn t have to beserializable and is not even processed by ObjectStateFormatter.

 

Member

Description

IsTrackingViewState

Indicates whether the class is currently tracking the viewstate for changes. This property is typically implemented returning an internal boolean variable.

LoadViewState

Populates a new instance of the class with the data stored in the object it receives.

SaveViewState

Saves the current state of the class to an object, usually a pair, a triplet, or an array. This object will be then passed to LoadViewState on the next postback.

TrackViewState

Begins tracking the viewstate for changes. This method is typically implemented by setting the boolean set behind the IsTrackingViewState property.

Figure 4: Membersof the IStateManager interface.

 

However, IStateManager simply states that the class cansave to the viewstate. What takes care of writing bytes to the viewstate? It isthe control itself through a couple of overridden methods, SaveViewState andLoadViewState.

 

Any custom control that has properties of complex typesmust override SaveViewState and LoadViewState to add the bytes of the propertyto its own copy of the viewstate. Figure 5 shows the typical code ofSaveViewState and LoadViewState for a custom control.

 

protected override object SaveViewState()

{

     object state =base.SaveViewState();

     object extra =Items.SaveViewState();

     return newPair(state, extra);

}

protected override void LoadViewState(object savedState)

{

     if (savedState !=null)

     {

           Pair p = (Pair)savedState;

           base.LoadViewState(p.First);

           Items.LoadViewState(p.Second);

     }

}

Protected Overrides Function SaveViewState() As Object

     Dim state As Object =MyBase.SaveViewState

     Dim extra As Object =Me.Items.SaveViewState

     Return NewPair(state, extra)

End Function

Protected Overrides Sub LoadViewState(ByVal savedState As Object)

     If Not savedState IsNothing Then

           Dim p As Pair =DirectCast(savedState, Pair)

           MyBase.LoadViewState(p.First)

           Me.Items.LoadViewState(p.Second)

     End If

End Sub

Figure 5: ViewStatemanagement on a custom control.

 

Conclusion

Control properties need a storage medium and typically usethe ViewState container for this purpose. Using the ViewState base property isefficient only if the type of the control is one that the ASP.NETinfrastructure knows how to handle. If your properties are of a complex type suchas a collection of custom classes the best that you can do is persisting theproperty through a local variable and instructing the class to serialize to theviewstate. In addition, you can override a couple of methods on the control toadd the bytes of the class to the control viewstate.

 

Dino Esposito is aSolid Quality Learning mentor and the author of ProgrammingMicrosoft ASP.NET 2.0 (Microsoft Press, 2005). Based in Italy,Dino is a frequent speaker at industry events worldwide. Join the blog at http://weblogs.asp.net/despos.

 

 

 

Sign up for the ITPro Today newsletter
Stay on top of the IT universe with commentary, news analysis, how-to's, and tips delivered to your inbox daily.

You May Also Like