How to bind an ASP.NET Repeater control (C#)
The Repeater is the simplest data-bound control in Web Forms: it renders a template once per item and adds nothing of its own. No table, no styles, no paging. That makes it ideal for lists, cards and any markup the GridView cannot produce.
Markup
<asp:Repeater ID="rptArticles" runat="server" ItemType="Demo.ArticleSummary"
OnItemDataBound="rptArticles_ItemDataBound" OnItemCommand="rptArticles_ItemCommand">
<HeaderTemplate><ul class="articles"></HeaderTemplate>
<ItemTemplate>
<li>
<a href='<%# "Article.aspx?id=" + Item.Id %>'><%#: Item.Title %></a>
<time datetime='<%# Item.Published.ToString("yyyy-MM-dd") %>'><%# Item.Published.ToString("d MMM yyyy") %></time>
<asp:Label ID="lblNew" runat="server" CssClass="badge" Text="New" Visible="false" />
<asp:LinkButton runat="server" CommandName="Archive" CommandArgument='<%# Item.Id %>' Text="Archive" />
</li>
</ItemTemplate>
<FooterTemplate></ul></FooterTemplate>
</asp:Repeater>
<asp:PlaceHolder ID="phEmpty" runat="server" Visible="false"><p>No articles yet.</p></asp:PlaceHolder>
Templates: HeaderTemplate and FooterTemplate render once, ItemTemplate per item, AlternatingItemTemplate (optional) for every second item, SeparatorTemplate between items. Note that the opening <ul> in the header and the closing tag in the footer form one element in the output.
ItemType (.NET 4.5 and later) gives strongly typed access through Item, checked at compile time. Without it, use Eval("Title"), which resolves the property by name at run time.
Code-behind
public class ArticleSummary
{
public int Id { get; set; }
public string Title { get; set; }
public DateTime Published { get; set; }
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack) Bind();
}
private void Bind()
{
var list = new List<ArticleSummary>();
using (var con = new SqlConnection(ConfigurationManager.ConnectionStrings["Db"].ConnectionString))
using (var cmd = new SqlCommand(
"SELECT TOP (20) ArticleId, Title, PublishedOn FROM dbo.Articles WHERE Archived = 0 ORDER BY PublishedOn DESC", con))
{
con.Open();
using (var r = cmd.ExecuteReader())
while (r.Read())
list.Add(new ArticleSummary { Id = r.GetInt32(0), Title = r.GetString(1), Published = r.GetDateTime(2) });
}
rptArticles.DataSource = list;
rptArticles.DataBind();
phEmpty.Visible = list.Count == 0;
}
protected void rptArticles_ItemDataBound(object sender, RepeaterItemEventArgs e)
{
if (e.Item.ItemType != ListItemType.Item && e.Item.ItemType != ListItemType.AlternatingItem) return;
var a = (ArticleSummary)e.Item.DataItem;
e.Item.FindControl("lblNew").Visible = a.Published > DateTime.Today.AddDays(-7);
}
protected void rptArticles_ItemCommand(object source, RepeaterCommandEventArgs e)
{
if (e.CommandName == "Archive" && int.TryParse((string)e.CommandArgument, out int id))
{
ArticleRepository.Archive(id);
Bind();
}
}
ItemDataBoundruns for every template, including header and footer, hence theItemTypecheck before readingDataItem.ItemCommandcatches the commands of all buttons in the items.CommandArgumentarrives as a string.- The Repeater has no
EmptyDataTemplate; the placeholder handles the empty case.
Paging is not built in; see Paging a Repeater. For a table with editing and sorting, the GridView is the better fit: Bind a GridView.
Official documentation
- Microsoft Learn: Repeater class, Repeater.ItemDataBound, Repeater.ItemCommand
- Microsoft Learn: Strongly typed data controls and model binding