It Could Be Done!

the blog about ASP.NET and not only

Control Builders & ASP.NET Generic Control Classes

Since the release of .NET 2.0 and ASP.NET 2.0 I was very disappointed that new language power of C# 2.0 is not available for ASP.NET developers.  It was stressed at the time of release by multiple people (see here, here and here).  While generic classes are not supported in ASP.NET markup in general, it seemed to me that it is still possible to benefit from generic in ASP.NET in many cases.

ASP.NET supports "control builders" abstraction controlling different aspects of code generation.  The most important is that control builders define the actual type of page field for the control and actual class of the control to be instantiated. 

Moreover, if control builder returns generic type, ASP.NET generates code which successfully instantiate it and page field is also correct. 

As a small sample I created a generic enumeration drop-down list control.  This control has a TypedValue property returning the currently selected enumeration member.  Type of this property is controlled from the EnumType attribute in the ASPX markup.  The following markup

  <t:EnumDropDown runat="server" EnumType="MyEnum" ID="TheDropDown" />

 

allows to use TheDropDown in code behind in a statically typed manner:

   MyEnum? atr = TheDropDown.TypedValue;

If you find the temporary C# file for the web page, you can see that TheDropDown field is declared as:

   protected global::CouldBeDone.GenericControls.EnumDropDown<MyEnum>
                                                              TheDropDown;


The trick in getting this behavior is in custom control builder class which tells ASP.NET to use generic version of the class instead of non-generic.  So, at first I created a non-generic version of EnumDropDown and decorated it with ControlBuilder attribute.

    [ControlBuilder(typeof(EnumDropDownControlBuilder))]
    public class EnumDropDown : DropDownList {
        private string m_EnumType;
        public string EnumType {
            get {
                return m_EnumType;
            }
            set { m_EnumType = value; }
        }
    }

The generic version of EnumDropDown which is used in web pages provides all useful features (like type Value property).

    public class EnumDropDown<T> : EnumDropDown
        where T : struct {

        public T? TypedValue {
          //// implementation removed
        }
    }

The EnumDropDownControlBuilder does only one thing.  It replaces the type of control to be instantiated.

    public override void Init(TemplateParser parser, ControlBuilder
              
parentBuilder, Type type,

               
string tagName, string id, IDictionary attribs) {
        string enumTypeName = (string)attribs["EnumType"];
 
       Type enumType = Type.GetType(enumTypeName);
        Type dropDownType =
               typeof(EnumDropDown<>).MakeGenericType(enumType);

        base.Init(parser, parentBuilder, dropDownType,
                  tagName, id, attribs);

    }

This was a very simple case.   The most important place where I like to see static typing in ASP.NET is data binding. 
I tried to apply this technique to get typed Repeater and new ListView controls and stop using Eval and type casting in data binding expressions.  I managed to get a result, but unfortunately it requires wrapping templates into special placeholder controls.  I will probably make one more post about generic classes in ASP.NET, if I can get typed data binding more simple.

If you like to see live EnumDropDown, here is the sample Visual Studio project:

GenericEnumDropDown.zip

The web site was create in beta 2 of Visual Studio 2008, but as it targets .net 2.0, I hope it should run fine in VS 2005 as well.

Posted: Oct 02 2007, 12:07 AM by ysw | with 13 comment(s)
Filed under:

Comments

James Crowley said:

Had no idea you could do that with control builders - neat!

# October 8, 2007 4:27 PM

James Crowley said:

Had no idea you could do that with control builders - neat!

# October 8, 2007 4:28 PM

karsten said:

I tried your code in an web application. One problem I have is that the code generated for the .designer.cs file is not able to build. The generic type seems to be missing a global:: in front of it.

# November 7, 2007 7:46 AM

Phil said:

I got the same "problem" about the designer.cs file and generic control definition (previously state by karsten).

Is there any solution about this one?

Thanks

Phil

# January 7, 2008 1:29 PM

ysw said:

Phil,

Actually I use ASPX files without code behind file.  It is fine for me as they are primarily views in MVC.  I haven't tried to find a solution for Web Application Projects and .designer.cs files.  

In spent some time trying to make this approach work for statically typed data binding in website model.  I got some results, but I could not find any generic approach which will work for all data bound controls.  

# January 7, 2008 3:23 PM

Florent said:

It works for simple custom controls.

If your generic control inherit from an abstract class, it doesn't work... for example, if it's inherit from BaseDataBoundControl class, an exception is thrown...

Somebody know how to pass through this ?

# April 24, 2008 6:10 PM

ysw said:

I think it should help if you introduce some intermediate non-abstract class before your generic control. Tools may try to instantiate base class without your control builder.

# April 25, 2008 7:05 AM

Amin said:

Please give me simple project

# July 28, 2010 4:52 AM

ysw said:

# November 10, 2010 11:33 AM

weblogs.asp.net said:

Control builders amp asp net generic control classes.. Super :)

# April 1, 2011 2:18 AM

weblogs.asp.net said:

Control builders amp asp net generic control classes.. Peachy :)

# April 28, 2011 1:28 AM

weblogs.asp.net said:

Control builders amp asp net generic control classes.. Awful :)

# June 11, 2011 1:24 AM

mike said:

How the code of a generic user control nesting another generic user control would look like?

Also, what if the generic user control contains a datalist having item template containing a generic user control?

# September 9, 2011 4:32 AM