The Repeater Control

The Repeater Control

The Repeater control is a simple container control that binds to a list of items. It walks through the bound items and produces graphical elements according to a basic rendering algorithm and the HTML templates you supply. The Repeater control supports from one through five templates. These templates, which form a tabular structure, are described in Table 1-2.

When you call DataBind on the Repeater control, the control first attempts to locate the HeaderTemplate and, if found, applies it. The control then loops through the list items and applies the ItemTemplate to each one. When the AlternatingItemTemplate is defined, odd rows take it. If the SeparatorTemplate is found, the template is applied in between two consecutive items, regardless of whether the template item is normal or an alternate. When the FooterTemplate is specified, it is applied at the end of the loop.

The Repeater control has no built-in layout facility or predefined style that you can apply by using a declaration, nor does it support selection and editing. You could manually code selection and editing, but for those tasks, you are better off dropping the Repeater control in favor of the more powerful DataList and DataGrid controls. You’ll learn more about these controls later in the chapter.

Accessing Data Bound Information

The Items property of the Repeater control is a collection of RepeaterItem objects, each representing an individual row in the list. To define the template structure for normal and alternating items, you need a way to access the data source fields for the particular row being processed. This information is exactly what the expression Container.DataItem returns when you use Container.DataItem in a data-binding expression for the normal or alternate templates.

Container.DataItem represents the nth object bound to the RepeaterItem objects in a list. It evaluates to the same type of element as the caller placed in the Repeater control’s data source. Let’s look at some examples. If you place a DataTable object in the DataSource property of the Repeater control, then Container.DataItem evaluates to a DataRow object. It evaluates to DataRow because in .NET, a DataTable is perceived as a collection of rows and each row is implemented through a DataRow object. If you use a DataView object, the individual item is evaluated to a DataRowView object. If you use, say, an array of strings, Container.DataItem evaluates to a simple text string. Table 1-3 shows how to obtain the value of a field for the current row.

Table 1-3 Ways to Retrieve Data Item Values in Iterative Controls

Data Source

Data Item


<%# ((DataRow) Container.DataItem)["Field"] %>


<%# ((DataRowView) Container.DataItem)["Field"] %>

Array of strings

<%# ((String) Container.DataItem) %>


<%# ((Dictionary) Container.DataItem)["entry"] %>

Because .NET objects are strongly typed, coercing types is an absolute necessity. If you find the syntax in Table 1-3 somewhat quirky, you can alternatively resort to the DataBinder class. This class features a static method named Eval that has an easier syntax to remember than that of standard data-binding expressions. When you use DataBinder.Eval to get the current data item in the expressions in Table 1-3, the code looks like this:

<%# DataBinder.Eval(Container.DataItem, "Field") %>

As you can see, DataBinder.Eval blurs the distinction between the element types in the control’s data source. However, because of automatic type inference and conversion, the housekeeping code for DataBinder.Eval can result in a slightly slower server response.

The DataBinder.Eval method has two prototypes:

public static Object Eval(Object container, String expr);
public static String Eval(Object container, String expr, String format);

The first argument identifies the data provider to which the next expression must be applied. The third argument, which is optional, specifies a format string that converts the results of evaluating the data-binding expression to String. When you need to apply formatting, DataBinder.Eval helps considerably to build a more readable statement. The following two instructions obtain the same result—formatting a numeric column as a currency value—but the first is easier to read and understand, albeit slightly slower:

<%# DataBinder.Eval(Container.DataItem, "Price", "{0:c}") %>
<%# String.Format("{0:c}", ((DataRowView) Container.DataItem)["Price"]) %>

Using DataBinder.Eval does not give you more programming power. It just makes programming easier because it shields you from many of the casting and formatting details. In return for this, it adds a little more overhead to your code, so try to use it only when necessary, such as when you need formatting.

Repeater Control Events

In addition to firing the standard events supported by all .NET controls (such as Init, Load, PreRender, and DataBinding), the Repeater control can fire three of its own events, which are described in Table 1-4.

Table 1-4 Events Supported by the Repeater Control




Fires whenever a new item is created, regardless of its type (header, footer, item, or separator) or its ordinal position.

ItemCreated(Object s, 
    RepeaterItemEventArgs e)

The event structure makes available to your code the current item (the Item property), the index (the ItemIndex property), the type (the ItemType property), and the DataItem associated with it (the DataItem property).


Fires whenever a user clicks a link button or a push button embedded in the control’s template.

ItemCommand(Object s, RepeaterItemEventArgs e)

The event structure provides the CommandSource property to let you know about the button that triggered the event.


Fires when an item is data bound, always before it is rendered.

ItemDataBound(Object s, RepeaterItemEventArgs e)

The ItemCommand event interface deserves a little more explanation because it is a peculiarity of iterative control and, like the Repeater control, is widely used with the DataList and DataGrid controls. ItemCommand fires whenever the user clicks any button in the Repeater control. You retrieve the current instance of the clicked object by using the CommandSource property. You can give each button control a name that identifies its action. You retrieve this name by using the CommandName property of RepeaterItemEventArgs.

Figure 1-4 shows an HTML table that was generated by using the Repeater control. This figure displays a few fields selected from the Northwind Employees database. (The full source code for the EmployeeList.aspx application is available on the companion CD.)

Figure 1-4
A data bound HTML table created using the Repeater control.

When the user clicks the Search button on the screen in Figure 1-4, the page runs a query against SQL Server and stores the default view of the retrieved table as the data source of the Repeater control.

Repeater has the following structure:

<asp:repeater runat="server" id="Repeater1">
    <HeaderTemplate> ... </HeaderTemplate>
    <ItemTemplate> ... </ItemTemplate>
    <AlternatingItemTemplate> ... </AlternatingItemTemplate>
    <SeparatorTemplate> ... </SeparatorTemplate>
    <FooterTemplate> ... </FooterTemplate>

For each template, you provide the ASP.NET code that will be evaluated dynamically during the execution of the DataBind method. You don’t need to specify all the templates. However, for a reasonably complex control, you need to specify at least the header and the item. Let’s take a look at each of the templates for the example in Figure 1-4.

The Header template is processed only once and is responsible for opening the table and defining the caption.

<table style="border:1px solid black;" class="stdtext">
    <thead bgcolor="blue" style="color:white">
        <td><b>First Name</b></td>
        <td><b>Last Name</b></td>

If you don’t define the same number of headers as expected columns, the graphical effect will be poor but you won’t get run-time errors.

Items and alternating items are element types that the control can represent with different graphical settings. Suppose your goal is to build an HTML table. As the following code illustrates, the item templates can add only the necessary number of rows and cells for the overall buffer.

        <td bgcolor="white"> <%# DataBinder.Eval(Container.DataItem, 
            "EmployeeID") %> </td>
        <td bgcolor="white"> <%# DataBinder.Eval(Container.DataItem,
            "FirstName") %> </td>
        <td bgcolor="white"> <%# DataBinder.Eval(Container.DataItem, 
            "LastName") %> </td>

        <td bgcolor="lightblue"> <%# DataBinder.Eval(Container.DataItem, 
            "EmployeeID") %> </td>
        <td bgcolor="lightblue"> <%# DataBinder.Eval(Container.DataItem,
            "FirstName") %> </td>
        <td bgcolor="lightblue"> <%# DataBinder.Eval(Container.DataItem, 
            "LastName") %> </td>

For any of the fields, an alternative approach would be to use more direct syntax and avoid DataBinder.Eval, as shown here:

<%# ((DataRowView) Container.DataItem)["LastName"] %>

Like the Header template, the Footer template is drawn only once and only when all items are rendered.

        <td bgcolor="silver" colspan="3">
            <%# "<b>" + ((DataView) Repeater1.DataSource).Count + 
                "</b> employees found." %> 

The Footer template defines the final row by using a <tfoot> HTML tag and makes sure that the final row spans all columns. Typically, you use the footer to display summary information. To obtain the total number of employees ­displayed, the code resorts to the Count property of the DataView class. To get the same information with a DataTable set as the data source, you would use the following expression:

<%# ((DataTable) Repeater1.DataSource).Rows.Count + 
    " employees found." %>