Click here to Skip to main content
15,070,111 members
Articles / Web Development / ASP.NET
Posted 16 Mar 2007


87 bookmarked

A Typed Repeater in ASP.NET

Rate me:
Please Sign up or sign in to vote.
4.89/5 (43 votes)
17 Mar 20074 min read
Hacking ASP.NET to build a Repeater with generics support

Limits of data-binding expressions

I think that the ASP.NET data-binding expressions (<%# %>) are just great. They're simple, powerful, intellisensed and checked at compile time. They reduce everyday coding efforts but do not pollute presentation with any heavy logic.

The most popular use of <%# %> expressions is the definition of template contents. Repeater and GridView are the controls where I see <%# %> most often.

But there are some problems with using these expressions within templates. The most interesting thing to databind within a template is the data item of the row template is instantiated in.

This data item is accessible through the IDataItemContainer interface that is implemented by each RepeaterItem and GridViewRow. Within the template expressions, a container is available as a Container variable or through the Eval/Bind pseudo-functions.

This interface inherited the .NET 1.0/1.1 fundamental problem - IDataItemContainer.DataItem is untyped. Let's see an example for the Repeater control: imagine that you have a Person business entity class:

public class Person
    private string name;
    private string email;

    public string Name
        get { return name; }
        set { name = value; }

    public string EMail
        get { return email; }
        set { email = value; }

And you want to show a list of Persons in a Repeater. There are two ways to do it: use Eval or Cast DataItem. Let's examine both.

  1. Eval:
    <asp:Repeater ID="repeaterWithEval" runat="server">
          <%# Eval("Name") %>: <%# Eval("EMail")%>

    This is the fastest and gravely wrong way. Minor problem: no intellisense on property names. Major problem: imagine someone decides EMail should be now called Mail. You change it, compile everything, but if you have a lot of pages and lazy test team or no test team at all. In such a case, ages may pass till somebody notices this page does not work anymore.

  2. Cast:
    <asp:Repeater ID="repeaterWithCast" runat="server">
          <%# (Container.DataItem as Person).Name %>: 
                <%# (Container.DataItem as Person).EMail %>

    This is safe (and even highlighted), but now it is way too verbose – just think about writing all these casts for ten properties. And what if your business entity is called AReallyLongCalledClass<OtherClass>?

    Obviously, the way it should work is:

    <asp:Repeater ID="repeaterWithHack" runat="server" 
          <%# Container.DataItem.Name %>: <%# Container.DataItem.EMail %>

    Fortunately, there is a way to make it work.

Time to hack ASP.NET

How to hack ASP.NET (in four steps):

  1. Download and install Reflector
  2. Find out what code does the thing you want to change
  3. Find the smallest hack needed to make it work as you want
  4. Write hack and enjoy results

The code of interest to us is TemplateContainerAttribute and ControlBuilder class. ControlBuilder examines the attribute to find out the type of the generated Container variable. The attribute is applied to the template property we are using:

public virtual ITemplate ItemTemplate

The smallest change was somewhat complex in this case, but still quite small. What I needed was to change the type of container in TemplateContainerAttribute depending on the DataItemTypeName specified in Repeater markup. This means that there is no actual way to specify this with an attribute – TemplateContainerAttribute is sealed so I cannot put any dynamic logic into it. Instead, I intercept the GetCustomAttributes() call on the ItemTemplate property and return a new attribute with the correct type.

But before going to the interception, let's build the generic classes that will serve as Container and new Repeater. There are three classes:

  1. Generic RepeaterItem
    public class RepeaterItem<TDataItem> : 
        public RepeaterItem(int itemIndex, ListItemType itemType) : 
                        base(itemIndex, itemType)
        public new TDataItem DataItem
            get { return (TDataItem)base.DataItem; }
            set { base.DataItem = (TDataItem)value; }
  2. Generic Repeater
    public class Repeater<TDataItem> : Repeater
        protected override RepeaterItem CreateItem(int itemIndex, 
                            ListItemType itemType)
            return new RepeaterItem<TDataItem>(itemIndex, itemType);
  3. Subclassed Repeater
    public class Repeater : System.Web.UI.WebControls.Repeater
        private string dataItemTypeName;
        public string DataItemTypeName
            get { return dataItemTypeName; }
            set { dataItemTypeName = value; }

    Subclassed Repeater is required since ASP.NET markup does not understand generics. But it is quite easy to trick ASP.NET to use the ControlBuilder from Repeater to actually build Repeater<T>. I will explain it while talking about interception.

    And interception is not as hard as it sounds. There are three steps to do it:

    1. Create a custom Type that will wrap typeof(Repeater<TDataItem>) and intercept the request of ItemTemplate property.

      This is actually very easy – Microsoft provides the TypeDelegator class to wrap any Type.

      So I just inherited TypeDelegator and overwrote the GetPropertyImpl method to wrap ItemTemplate PropertyInfo into FakePropertyInfo.

      internal class RepeaterFakeType : TypeDelegator
          private class FakePropertyInfo : PropertyInfoDelegator
          private Type repeaterItemType;
          public RepeaterFakeType(Type dataItemType)
              : base(typeof(Repeater<>).MakeGenericType(dataItemType))
              this.repeaterItemType = typeof(RepeaterItem<>).MakeGenericType(dataItemType);
          protected override PropertyInfo GetPropertyImpl(string name, …)
              PropertyInfo info = base.GetPropertyImpl(name, …);
              if (name == "ItemTemplate")
                  info = new FakePropertyInfo(info, this.repeaterItemType);
              return info;

      The code is quite easy and self-documenting. One interesting thing is that it receives the DataItemType and then presents itself as a correct Repeater<> type with the MakeGenericType method.

    2. Create custom PropertyInfo to override the GetCustomAttributes method. This was a bit harder since there is no predefined PropertyInfoDelegator. So I just built one and then inherited it:

      private class FakePropertyInfo : PropertyInfoDelegator
          private Type templateContainerType;
          public FakePropertyInfo(PropertyInfo real, 
                  Type templateContainerType) : base(real)
              this.templateContainerType = templateContainerType;
          public override object[] GetCustomAttributes
                      (Type attributeType, bool inherit)
              if (attributeType == typeof(TemplateContainerAttribute))
                  return new Attribute[] 
          { new TemplateContainerAttribute(templateContainerType) };
              return base.GetCustomAttributes(attributeType, inherit);

      This code is also quite straightforward.

    3. Create the RepeaterControlBuilder that substitutes the RepeaterFakeType in place of typeof(Repeater). That was the easiest part, just overriding Init:

      public class RepeaterControlBuilder : ControlBuilder
          public override void Init(TemplateParser parser,
                                    ControlBuilder parentBuilder,
                                    Type type,
                                    string tagName,
                                    string id,
                                    IDictionary attribs)
              string dataItemTypeName = attribs["DataItemTypeName"] as string;
              Type dataItemType = BuildManager.GetType(dataItemTypeName, true);
              Type repeaterFakeType = new RepeaterFakeType(dataItemType);
              base.Init(parser, parentBuilder, repeaterFakeType, 
                              tagName, id, attribs);

And it just works

With all of this, I can now write

<my:Repeater ID="repeater" runat="server" 
    <div><%# Container.DataItem.Name %> : 
                <%# Container.DataItem.EMail %></div>

I can also get intellisense and compile-time checks. The only thing left is intellisense in DataItemTypeName attribute, but this is definitely a minor issue and I'll think about this later. Meanwhile, you can download the code and play with it.


  • 17.03.2007: Posted to The Code Project


This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


About the Author

Andrey Shchekin
Web Developer
Russian Federation Russian Federation
No Biography provided

Comments and Discussions

GeneralMy vote of 5 Pin
Menon Hari10-May-12 8:25
MemberMenon Hari10-May-12 8:25 
GeneralNo intellisense VS2010 Pin
TheCodeKing18-Mar-11 23:41
MemberTheCodeKing18-Mar-11 23:41 
GeneralRe: No intellisense VS2010 Pin
earlam19-Sep-11 21:59
Memberearlam19-Sep-11 21:59 
GeneralMy vote of 5 Pin
Member 198182420-Feb-11 22:11
MemberMember 198182420-Feb-11 22:11 
General.NET v4 Fix (by Mueez Siddiqui) Pin
Danila Korablin8-Mar-10 19:32
MemberDanila Korablin8-Mar-10 19:32 
GeneralRe: .NET v4 Fix (by Mueez Siddiqui) Pin
hollowMedia [0xFA7E]10-Aug-10 11:35
MemberhollowMedia [0xFA7E]10-Aug-10 11:35 
QuestionTyped DataItem in TemplateField? Pin
Member 79931630-Dec-09 23:39
MemberMember 79931630-Dec-09 23:39 
GeneralYou saved my day Pin
Alomgir Miah A6-Mar-09 4:43
MemberAlomgir Miah A6-Mar-09 4:43 
QuestionCompile time checking in Web Application? Pin
azzamitch12-Nov-08 1:18
Memberazzamitch12-Nov-08 1:18 
AnswerRe: Compile time checking in Web Application? Pin
Cestbienmoi19-Dec-08 5:07
MemberCestbienmoi19-Dec-08 5:07 
GeneralWeb Application Projects - fix Pin
Roasted Amoeba19-Jun-08 1:49
MemberRoasted Amoeba19-Jun-08 1:49 
GeneralThank you Pin
Ahura Mazda8-May-08 1:18
MemberAhura Mazda8-May-08 1:18 
Generalproblem with Web Application Project Pin
mike96324-Mar-07 0:31
Membermike96324-Mar-07 0:31 
GeneralRe: problem with Web Application Project Pin
Andrey Shchekin25-Mar-07 10:22
MemberAndrey Shchekin25-Mar-07 10:22 
Questionhow to avoid string in DataItemTypeName property Pin
Serpenter_e21-Mar-07 20:47
MemberSerpenter_e21-Mar-07 20:47 
AnswerRe: how to avoid string in DataItemTypeName property Pin
Andrey Shchekin21-Mar-07 20:50
MemberAndrey Shchekin21-Mar-07 20:50 
GeneralBrilliant Pin
Marc Brooks19-Mar-07 20:39
MemberMarc Brooks19-Mar-07 20:39 
GeneralRe: Brilliant Pin
Andrey Shchekin20-Mar-07 2:31
MemberAndrey Shchekin20-Mar-07 2:31 
GeneralThanks Pin
Omari O.18-Mar-07 23:39
MemberOmari O.18-Mar-07 23:39 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.