Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / CSS

Support for Generics in ASP.NET Server Controls

4.77/5 (16 votes)
29 Jan 2009CPOL4 min read 62.4K   446  
Implementation of a framework to support Generics in ASP.NET server controls, including strong-typing of ITemplate containers.

Generics in ASP .NET? Why?

Anyone who has ever built a WebForms based application has no doubt come across the well-worn Repeater control. A staple of web programming, it allows for the display of a collection of items, and exposes the items through the Eval or Bind methods.

Under the hood, Eval and Bind work through Reflection in order to provide late binding support. This is also what you see if you reference properties such as:

XML
<%# Container.DataItem.PropertyNameHere %>

The reason for this is that ASP.NET allows transparent late binding, so even though it believes that Container.DataItem is typed as 'Object', the evaluation will only occur at runtime. This leads to all sorts of problems such as:

  • Pages breaking when a property is changed, and no warnings at compile-time.
  • Reduced performance, as every data-field display point becomes a Reflection call.

If we could tell ASP.NET what the type of the data items actually are, though, then during site start-up/compilation, we would be able to ensure the right types are being used, and no late-binding ever occurs. Compile time checking of all pages, master-pages, and user control binding tags sounds good?

If that's not enough for you, what about full intellisense support?

StrongTypingIntellisense.png

Generics in ASP.NET? How?

During start-up and the first hit to a page in a given folder, ASP.NET performs batch compilation of your ASPX files. That is, the conversion of them and the content therein into actual .NET code that will be run. The trouble previously with ASP.NET Generics was that there is no syntax support in ASP.NET to specify a generic type, for example:

ASP.NET
<asp:Repeater ID="Repeater1" runat="Server" />

is all well and good, but you can't say:

ASP.NET
<asp:GenericRepeater<String> ID="StringRepeater" />

As a result, we're going to have to find a workaround. I've ended up taking a very similar route to another tutorial that gives a typed Repeater implementation, by Andrey Shchekin. You can find his tutorial here. The way this works is as follows:

  • We intercept compile-time construction of pages using a ControlBuilder sub-class assigned to a non-generic class.
  • This ControlBuilder's Init method switches the non-generic type for a real generic type, using type-names read from properties of the non-generic control.
  • We wrap the real type inside a TypeDelegator that intercepts any calls to properties of the type (such as the templated containers).
  • Whenever ASP.NET requests the TemplateContainer attribute of the type, we give it back a dynamically constructed instance that contains the correct strong-typing information.

In the example code, I've created a 'GenericRepeater' that displays a strongly typed collection and the items therein. I have then sub-classed that GenericRepeater with <Object, Object> in order to create an ObjectRepeater like so:

C#
[ControlBuilder(typeof(GenericTemplatedControlBuilder))]
[GenericControlType(typeof(GenericRepeater<,>), "CollectionTypeName", "ItemTypeName")]
public class ObjectRepeater : GenericRepeater<IEnumerable<Object>, Object>

The ObjectRepeater inherits the real generic repeater type, and the attributes tell ASP.NET to use the special control builder instead of the base one. The GenericTemplatedControlBuilder then looks for the GenericControlType attribute, and will activate the 'real' GenericRepeater instance with the two type parameters.

Inside the generic repeater implementation, we mark up the various ITemplate properties with other attributes:

C#
[^__strong__^GenericTemplateContainerParameter(typeof(IndexedDataContainer<,>), 0, 1)]
[PersistenceMode(PersistenceMode.InnerProperty)]
public ITemplate ItemTemplate { get; set; }

The GenericTemplateContainerParameter declaration tells the system what type of data container to instantiate, and also the indices of the generic parameters to pass down. Thus, since IndexedDataContainer takes two generic parameters, we are telling to take generic parameters 0 (TCollectionType) and 1 (TItemType) from the GenericRepeater as the relevant parameters.

When ASP.NET scans the properties of the control, we have now switched the control for a generic control and instructed our GenericControlPropertyDelegator to remap all calls for TemplateContainer to our real, generic template containers.

The Final Product

Now that we have everything in play, let's define a simple ASP.NET page that uses the control:

StrongTypingRenderedOutput.png

And, there you have it, a simple generics framework for ASP.NET that allows you to deal with pretty much any generic control scenario, and is not specifically tied down to the example for use with Repeaters.

Notes About the Code

The example code supplied does not actually implement any post-back support, so don't be surprised if you need to add some code to load/save data in the viewstate and so forth.

Additionally, if you're going to reference this code on your own Web Applications, please ensure you take the web.config elements across to register the CFC tag prefix.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)