Click here to Skip to main content
15,885,244 members
Articles / Web Development / ASP.NET
Article

A Templated databound Repeater control with per-row template selection

Rate me:
Please Sign up or sign in to vote.
4.89/5 (67 votes)
9 Sep 20044 min read 324.5K   2.9K   143   42
This control allows you to select a template for each different row in your Repeater, based on the content of that row.

Introduction

This week I stumbled across a problem. I had to display the details of an order. Usually, I just grab a repeater and presto. But this time, there was something different. Some rows inside the order row collection were not order rows. For example, my collection would contain 3 OrderRows and one OrderDescription row.

I had to generate output like this:

Potatoes$12,99(remove)
Cabbage$10,00(remove)
(note that gabbage is delivered sliced)
Crates of beer$4,12(remove)

I'm not trying to have any discussion of the usefulness or the content of this example. The basic thing is that this cannot be easily done with a generic Repeater.

Intended Solution

I wanted to be able to dynamically decide which template to use for each element in my collection. This required me to be able to describe multiple templates in my .asp code:

ASP.NET
<cc:MyRepeater id="order" runat="server">
  <ItemTemplate forClass="OrderRow">
    <tr>
      <td><%# DataBinder.Eval(Container.DataItem, "Name")%></td>
      <td><%# DataBinder.Eval(Container.DataItem, "Price", "C")%></td>
      <td><asp:Button id="Remove" CommandName="remove" runat="server"/></td>
    </tr>
  </ItemTemplate>
  <ItemTemplate forClass="OrderDescription">
    <tr>
      <td colspan="3"><%# DataBinder.Eval(Container.DataItem, "Name")%></td>
    </tr>
  </ItemTemplate>
</cc:MyRepeater>

During my research, I came upon some pretty nasty stuff that didn't seem possible. After some tedious searching however, I found the solution.

Solution

I had to resort to a small change in the model described above. It was not only not possible to add properties to different ItemTemplates, it also wasn't possible to dynamically define ITemplate properties. I solved this problem by wrapping the different templates into subobjects, as follows:

ASP.NET
<cc:ObjectRepeater id="order" runat="server">
  <ObjectTemplate name="orderRow">
    <ItemTemplate>
      <tr>
        <td><%# DataBinder.Eval(Container.DataItem, "Name")%></td>
        <td><%# DataBinder.Eval(Container.DataItem, "Price", "C")%></td>
        <td><asp:Button id="Remove" CommandName="remove" runat="server"/></td>
      </tr>
    </ItemTemplate>
  </ObjectTemplate>
  <ObjectTemplate name="description">
    <ItemTemplate>
      <tr>
        <td colspan="3"><%# DataBinder.Eval(Container.DataItem, "Name")%></td>
      </tr>
    </ItemTemplate>
  </ObjectTemplate>
</cc:ObjectRepeater>

I also added a delegate to my ObjectRepeater that allows me to determine which template to select. This delegate can be assigned in your code behind. The delegate must return the name of the template to use for the item that is currently databound. For example:

C#
private void Page_Load(object sender, System.EventArgs e)

{
    order.DetermineTemplate = 
      new ObjectRepeaterDetermineTemplateDelegate(this.determineTemplate);
}

public string determineTemplate(object sender, object dataItem)

{
   if(dataItem is OrderRow)
      return "orderRow";
   else
      return "description";

}

This delegate can also be used to check certain properties of your DataItem, instead of just checking the class type.

Implementation Details

To be able to implement this solution, I had to develop a templated databound custom control as is described in MSDN quite well.

The problem can be divided into three parts:

  1. How to define and parse the different ObjectTemplates.
  2. How to dynamically determine the template to use.
  3. How to make the Repeater save its state in ViewState so postbacks work as expected.

Problem #1

To be able to dynamically add objects, I've overridden AddParsedSubObject() and I implemented a ControlBuilder. The ControlBuilder and AddParsedSubObject() work in team to remember the defined ObjectTemplates into a private collection named _templates.

Please look at the attached project for details, because this is not really rocket science.

The trick lies into adding an ITemplate property to the ObjectTemplate class, and including a [TemplateContainer(typeof(RepeaterItem))] attribute to that property. Because the ASP parser detects that my ObjectTemplate is also a control, it nicely turns this object into a template.

Problem #2

In the code for CreateItem, these is a point where a child row gets created:

C#
private RepeaterItem CreateItem(int itemIndex, 
      ListItemType itemType, bool dataBind, object dataItem)
{
  RepeaterItem item = new RepeaterItem(itemIndex, itemType);

  RepeaterItemEventArgs e = new RepeaterItemEventArgs(item);

  //decide which template to use.
  string templateName = null;

  if(dataBind)
  {
    if(DetermineTemplate != null)
    {
      templateName = this.DetermineTemplate(this, dataItem);
      ViewState["templateName" + itemIndex.ToString()] = templateName;
    }
  }
  else
  {
    //determine template to use from viewState;
    templateName = (string)ViewState["templateName" + itemIndex.ToString()];
  }

  if(templateName == null)
    templateName = this.DefaultTemplate;

  CustomDynamicTemplate dynamicTemplate = 
       (CustomDynamicTemplate)_templates[templateName];

  //Must exist.
  dynamicTemplate.ItemTemplate.InstantiateIn(item);
   
  if (dataBind)
  {
    item.DataItem = dataItem;
  }
  OnItemCreated(e);
  this.Controls.Add(item);

  if (dataBind)
  {
    item.DataBind();
    OnItemDataBound(e);

    item.DataItem = null;
  }

  return item;
}

For each row, I call a delegate (your delegate) to determine the name of the template. I then look it up in my HashTable of templates and do a InstantiateIn(). The rest of the code is very similar to Microsoft's example of databound templated custom controls.

Problem #3

The final problem lies in that the used template (per row) must be maintained in ViewState, otherwise postback event will not fire, or worst, will fire for the incorrect row.

In the above code, I maintain ViewState attributes so that the used template for each row is remembered. After postback, this ViewState value is retrieved and used instead.

Final Notes

I've looked around the internet for more on this specific subject, and found little or none about adding dynamic templates. Most people seemed to be stuck on a simpler problem, namely dynamically selecting templates for a whole DataGrid or Repeater, and not on a per-row basis. Personally, I see a lot of potential for this kind of a Repeater object, especially when you repeat amongst a collection of classes that may (or may not) be subclasses of each other. Your biggest advantage is not having to code all your variations in a server control (.cs class), or writing a lot of <% if .. %> code inside your template definitions.

In conclusion, I hope you liked this article, and maybe you have some use for this class as good as I did! If so, please vote.. (if you didn't, please vote as well).

License

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


Written By
Web Developer
Netherlands Netherlands
Rob has been a professional web developer since 1998, and is working with C# and ASP.Net since early 2002.

Most of the time, his focus is on creating a clean and simple solution to development problems. (Although that sometimes means that he needs to do some hard work to make somebody else's life more easier.).

Comments and Discussions

 
QuestionCode License Pin
MVukoje12-Apr-18 2:35
MVukoje12-Apr-18 2:35 
Generaloverload onitemdatabound Pin
dishmal4-Dec-06 10:39
dishmal4-Dec-06 10:39 
GeneralOverriding CreateItem of existing ASP.Net controls should be enough Pin
nono40014-Sep-05 21:49
nono40014-Sep-05 21:49 
GeneralRe: Overriding CreateItem of existing ASP.Net controls should be enough Pin
zbend29-Sep-05 6:32
zbend29-Sep-05 6:32 
GeneralRe: Overriding CreateItem of existing ASP.Net controls should be enough Pin
nono40030-Oct-05 23:48
nono40030-Oct-05 23:48 
GeneralRe: Overriding CreateItem of existing ASP.Net controls should be enough Pin
Rob van der Veer5-Nov-05 4:04
Rob van der Veer5-Nov-05 4:04 
GeneralRe: Overriding CreateItem of existing ASP.Net controls should be enough Pin
nono40025-Jan-06 23:42
nono40025-Jan-06 23:42 
QuestionHow about per cell template control? Pin
clibera9-Sep-05 10:19
clibera9-Sep-05 10:19 
QuestionVB code for this control? Pin
PuLk196-May-05 11:26
PuLk196-May-05 11:26 
GeneralYour code ;) Pin
tsmyrnio20-Dec-04 16:24
tsmyrnio20-Dec-04 16:24 
GeneralDesign Time Pin
gdjames20-Dec-04 5:02
gdjames20-Dec-04 5:02 
GeneralRepeater Pin
t_t_l10-Nov-04 23:44
t_t_l10-Nov-04 23:44 
Generalnested repeaters Pin
Anonymous3-Nov-04 14:37
Anonymous3-Nov-04 14:37 
GeneralRe: nested repeaters Pin
Rob van der Veer5-Nov-04 5:54
Rob van der Veer5-Nov-04 5:54 
GeneralTrivial Alternate Solution for ASP.NET 2.0 Pin
Michael K. Smith29-Oct-04 5:22
Michael K. Smith29-Oct-04 5:22 
GeneralRe: Trivial Alternate Solution for ASP.NET 2.0 Pin
Rob van der Veer30-Oct-04 1:22
Rob van der Veer30-Oct-04 1:22 
GeneralRe: Trivial Alternate Solution for ASP.NET 2.0 Pin
Anonymous30-Oct-04 6:55
Anonymous30-Oct-04 6:55 
GeneralRe: Trivial Alternate Solution for ASP.NET 2.0 Pin
Rob van der Veer31-Oct-04 6:57
Rob van der Veer31-Oct-04 6:57 
GeneralRe: Trivial Alternate Solution for ASP.NET 2.0 Pin
Fabian Lopez5-Dec-05 12:22
Fabian Lopez5-Dec-05 12:22 
GeneralRe: Trivial Alternate Solution for ASP.NET 2.0 Pin
BazzaUk8-Feb-06 5:59
BazzaUk8-Feb-06 5:59 
GeneralRe: Trivial Alternate Solution for ASP.NET 2.0 Pin
filmsthething12-Apr-06 14:53
filmsthething12-Apr-06 14:53 
GeneralRe: Trivial Alternate Solution for ASP.NET 2.0 Pin
Michael K. Smith12-Apr-06 17:22
Michael K. Smith12-Apr-06 17:22 
GeneralRe: Trivial Alternate Solution for ASP.NET 2.0 Pin
cyberbobcity719-May-06 10:25
cyberbobcity719-May-06 10:25 
I too am doing a survey/ questionaire engine. My question is where and how do I databind a radiobuttonlist control inside one of the templates?
QuestionRe: Trivial Alternate Solution for ASP.NET 2.0 Pin
gagirl4322-Feb-07 2:49
gagirl4322-Feb-07 2:49 
GeneralVisibleDate Problem Pin
vrao925-Oct-04 8:48
vrao925-Oct-04 8:48 

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.