Gal Ratner
Gal Ratner is a Techie who lives and works in Los Angeles CA and Austin TX. Follow galratner on Twitter Google
Create a faster Repeater

A Repeater, as we all know is the fastest of the data bound controls. It does not include any UI elements or even paging. This allows it to be light and fast.
What happens if even the Repeater is too heavy for our app? It’s simple, let’s write our own repeater.
The first step in our Repeater is its items class. We are going to hold our own data object; however, we still need to implement IDataItemContainer. This will help in data binding operations.
Let’s say our data object looks something like this:

public class MyDataObject

{

  public string FirstName { get; set; }

  public string LastName { get; set; }

}

 

Our Repeater item will hold this object as its data item like so:

 

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.ComponentModel;

using System.Web.UI;

 

namespace InvertedSoftware.CustomControls

{

    public enum FastRepeaterItemType

    {

        Header = 1,

        Footer = 2,

        Item = 3

    }

 

    [

    ToolboxItem(false)

    ]

    public class FastRepeaterItem : Control, IDataItemContainer

    {

        public FastRepeaterItem(int itemIndex, FastRepeaterItemType itemType)

        {

            this.itemIndex = itemIndex;

            this.itemType = itemType;

        }

 

        private MyDataObject dataItem;

 

        public virtual object DataItem

        {

            get

            {

                return dataItem;

            }

            set

            {

                dataItem = value as MyDataObject;

            }

        }

 

        public string FirstName

        {

            get

            {

                return dataItem.FirstName;

            }

        }

 

        public string LastName

        {

            get

            {

                return dataItem.LastName;

            }

        }

 

        private int itemIndex;

 

        public virtual int ItemIndex

        {

            get

            {

                return itemIndex;

            }

        }

 

        private FastRepeaterItemType itemType;

 

        public virtual FastRepeaterItemType ItemType

        {

            get

            {

                return itemType;

            }

        }

 

        int IDataItemContainer.DataItemIndex

        {

            get

            {

                return ItemIndex;

            }

        }

 

        int IDataItemContainer.DisplayIndex

        {

            get

            {

                return ItemIndex;

            }

        }

    }

}

 

 

The repeater itself is a Control and implements INamingContainer.
Why did I choose to extend the Control class? Well consider the alternatives: CompositeControl and CompositeDataBoundControl both derive from Control and i already have all of the functionality I need in Control. I decided to go with the lightest solution we can find.

[ParseChildren(true)]

public class FastRepeater : Control, INamingContainer


We need to override CreateChildControls in order to build the control.

 

protected override void CreateChildControls()

{

   Controls.Clear();

   if (ViewState[ItemCountViewStateKey] != null)

      CreateControlHierarchy(false);

   else

      dummyItems = new List<MyDataObject>();

 

      ClearChildViewState();

}

 

The next function CreateControlHierarchy is where the magic happens. This function is in charge of creating all of the items from the DataSource. It fires twice, once with a full data source, and once with a dummy data source we have created in order to keep the view state on postbacks.

 

 

protected virtual void CreateControlHierarchy(bool useDataSource)

{

  List<MyDataObject> dataSource = null;

  int count = -1;

  if (dummyItems != null)

    dummyItems.Clear();

  else

    dummyItems = new List<MyDataObject>();

 

  if (useDataSource == false)

  {

    count = (int)ViewState[ItemCountViewStateKey];

    if (count != -1)

    {

      dataSource = new List<MyDataObject>();

      while (dataSource.Count <= count)

         dataSource.Add(new MyDataObject());

    }

   }

   else

   {

    dataSource = DataSource;

   }

 

   if (dataSource != null)

   {

    FastRepeaterItem item;

    int index = 0;

    count = 0;

 

    AddHeader();

 

    foreach (object dataItem in dataSource)

    {

      item = CreateItem(index, FastRepeaterItemType.Item, useDataSource, dataItem);

      count++;

      index++;

    }

 

    AddFooter();

   }

 

    if (useDataSource)

      ViewState[ItemCountViewStateKey] = ((dataSource != null) ? count : -1);

  }

 

Notice we are in charge of saving the viewstate ourselves. All we need is the item count and that is what we are using in order to build the dummy data source.

 

Using the repeater on our page is simple:

 

<%@ Register assembly="ConceptLoop" namespace="InvertedSoftware.CustomControls" tagprefix="cc1" %>

<cc1:FastRepeater ID="FastRepeater1" runat="server">

 <HeaderTemplate>

   <table>

 </HeaderTemplate>

 <ItemTemplate>

   <tr>

    <td><%# Container.FirstName %></td>

    <td><%# Container.LastName %></td>

   </tr>

 </ItemTemplate>

 <FooterTemplate>

  </table>

 </FooterTemplate>

 </cc1:FastRepeater>

 And in the code behind:

 protected void Page_Load(object sender, EventArgs e)

 {

   if (!Page.IsPostBack)

   {

     FastRepeater1.DataSource = data;

     FastRepeater1.DataBind();

   }

 }

 

You can download the complete source code from the bottom of this post.
Enjoy!

 

Shout it


Posted 23 Feb 2010 6:26 AM by Gal Ratner
Filed under: , ,
Attachment: FastRepeater.cs

Powered by Community Server (Non-Commercial Edition), by Telligent Systems