woensdag 11 april 2012

Creating a strongly typed Repeater Control in ASP.NET using generics

The Repeater is one of my favorite and most used controls when developing websites in ASP.NET Webforms. It is the best tool at hand for generating dynamic lists based on collections of data. I'm also very fond of using databinding syntax in .aspx files avoiding code-behind files because in my opinion it increases readability and manageability.

One of the main drawbacks when using the Repeater in conjunction with databinding syntax is the need for casting the DataItem property to it's actual type:


I would like to share my take on a strongly typed Repeater which removes the need for casting objects, increases productivity and readability and unlocks intellisense!


The solution

The solution requires you to create two base classes which includes the core functionality and then for every typed Repeater create a simple inherited class which defines the type. As you might have noticed in the above screenshot I introduced a new property called TypedItem. You are free to create a name which suits your needs e.g. Container.I for improved readability.

Setting up the base classes:
1. Create a generic Repeater base class

This is the main workhorse of the solution. It was neccessary to override the InitializeItem method of the Repeater class because the default Repeater works with the private variables for the templates. This implementation uses the properties for each template which is required because we will be overriding the ItemTemplate and AlternatingItemTemplate for our typed Repeaters.

Also the CreateItem method is overriden to actually create a generic RepeaterItem<T> which will be created in step 2.

using System.Web.UI;
    using System.Web.UI.WebControls;

    public class GenericRepeaterBase<T> : Repeater where T : class
    {
        protected override RepeaterItem CreateItem(int itemIndex, ListItemType itemType)
        {
            return CreateRepeaterItem(itemIndex, itemType);
        }

        private static GenericRepeaterItem<T> CreateRepeaterItem(int itemIndex, ListItemType itemType)
        {
            return new GenericRepeaterItem<T>(itemIndex, itemType);
        }

        protected override void InitializeItem(RepeaterItem item)
        {
            ITemplate template = (ITemplate)null;
            switch (item.ItemType)
            {
                case ListItemType.Header:
                    template = HeaderTemplate;
                    break;
                case ListItemType.Footer:
                    template = FooterTemplate;
                    break;
                case ListItemType.Item:
                    template = ItemTemplate;
                    break;
                case ListItemType.AlternatingItem:
                    template = AlternatingItemTemplate;
                    if (template != null)
                        break;
                    else
                        goto case 2;
                case ListItemType.Separator:
                    template = SeparatorTemplate;
                    break;
            }
            if (template == null)
                return;
            template.InstantiateIn((Control)item);
        }
    }

2. Create a generic RepeaterItem base class

This class contains the property that you use while working with the Repeater. You can rename the TypedItem property as you wish.

using System.Web.UI.WebControls;

    public class GenericRepeaterItem<T> : RepeaterItem where T : class
    {
        public GenericRepeaterItem(int itemIndex, ListItemType itemType)
            : base(itemIndex, itemType)
        {

        }

        public T TypedItem
        {
            get { return (T)this.DataItem; }
        }
    }

Usage:
3. Create a typed Repeater

Now for every Repeater that you would like to use strongly typed you must create an easy implementation of the GenericRepeaterBase<T> class. The example below creates the PersonRepeater mentioned before.

The PersistenceMode attribute needs to be redefined because it is not automatically inherited. The TemplateContainer attribute and the generic type definition on the class make the neccessary definitions for the Repeater to work.

public class PersonRepeater : GenericRepeaterBase<GenericRepeaterItem<Person>>
    {
        [PersistenceMode(PersistenceMode.InnerProperty)]
        [TemplateContainer(typeof(GenericRepeaterItem<Person>))]
        public override ITemplate ItemTemplate { get; set; }

        [PersistenceMode(PersistenceMode.InnerProperty)]
        [TemplateContainer(typeof(GenericRepeaterItem<Person>))]
        public override ITemplate AlternatingItemTemplate { get; set; }
    }

4. Use it!

public class Person
    {
        public string Name { get; set; }
        public DateTime DateOfBirth { get; set; }
    }

<%@ Register TagPrefix="asp" Namespace="TypedRepeater" Assembly="TypedRepeater" %>
    <asp:PersonRepeater runat="server">
        <ItemTemplate>
            Name: <% #Container.TypedItem.Name %><br />
            Date of birth: <%#Container.TypedItem.DateOfBirth.ToShortDateString() %>
        </ItemTemplate>
    </asp:PersonRepeater>

I hope this code brings you lots of joy! ;)