title: Creating An HtmlTable Using An ASP.NET Repeater Dynamically Bound to an Object List
body: <p>I'm creating a table inside a repeater programmatically. My problem is that the cells in the item template part don't render properly. They are showing up as text nodes after the table's markup. Does anyone have any suggestions on how to get the table cells rendering properly?</p>
  
  <pre><code>using System;
  using System.Collections.Generic;
  using System.Web.UI;
  using System.Web.UI.HtmlControls;
  using System.Web.UI.WebControls;
  
  namespace WebApplication1
  {
      public partial class WebForm2 : Page
      {
          protected void Page_Load(object sender, EventArgs e)
          {
              if (!IsPostBack)
              {
                  var dogs = new List&lt;Dog&gt;
                  {
                      new Dog { Name = "Rex", Breed = "Russell Terrier" },
                      new Dog { Name = "Fido", Breed = "Poodle" },
                      new Dog { Name = "Fetcher", Breed = "Golden Retriever" },
                  };
  
                  var repeater = new Repeater { ID = "Repeater1" };
                  repeater.DataSource = dogs;
                  repeater.DataBind();
  
                  AddHeader(repeater);
                  AddItems(repeater, dogs);
  
                  PlaceHolder1.Controls.Add(repeater);
              }
          }
  
          private void AddHeader(Repeater repeater)
          {
              var repeaterItem = new RepeaterItem(0, ListItemType.Header);
              var table = new HtmlTable();
              var row = new HtmlTableRow();
              var cell1 = new HtmlTableCell("th") { InnerText = "Name" };
              var cell2 = new HtmlTableCell("th") { InnerText = "Breed" };
              row.Cells.Add(cell1);
              row.Cells.Add(cell2);
              table.Rows.Add(row);
              repeaterItem.Controls.Add(table);
              repeater.Controls.Add(repeaterItem);
          }
  
          private void AddItems(Repeater repeater, List&lt;Dog&gt; dogs)
          {
              for (var i = 0; i &lt; repeater.Items.Count; i++)
              {
                  var repeaterItem = new RepeaterItem(i + 1, ListItemType.Item);
                  var row = new HtmlTableRow();
                  var cell1 = new HtmlTableCell() { InnerText = dogs[i].Name };
                  var cell2 = new HtmlTableCell() { InnerText = dogs[i].Breed };
                  row.Cells.Add(cell1);
                  row.Cells.Add(cell2);
                  repeaterItem.Controls.Add(row);
                  repeater.Controls.Add(repeaterItem);
              }
          }
  
          private sealed class Dog
          {
              public string Breed { get; set; }
              public string Name { get; set; }
          }
      }
  }
  </code></pre>
  
  <p>UPDATE: Thanks to Rob's help, I was able to get the code working. Now I have a fully-functional Repeater being loaded entirely in the code-behind with data-binding. Nice!</p>
  
  <pre><code>using System;
  using System.Collections.Generic;
  using System.Linq;
  using System.Web.UI;
  using System.Web.UI.HtmlControls;
  using System.Web.UI.WebControls;
  using ExtensionMethods.WebControls;
  
  namespace WebApplication1
  {
      public partial class WebForm1 : Page
      {
          protected void Page_Load(object sender, EventArgs e)
          {
              var dogs = new List&lt;Dog&gt;
              {
                  new Dog { Name = "Rex", Breed = "Russell Terrier" },
                  new Dog { Name = "Fido", Breed = "Poodle" },
                  new Dog { Name = "Fetcher", Breed = "Golden Retriever" },
              };
  
              var repeater = new Repeater
              {
                  ID             = "Repeater1",
                  HeaderTemplate = new CustomTemplate(ListItemType.Header),
                  ItemTemplate   = new CustomTemplate(ListItemType.Item),
                  FooterTemplate = new CustomTemplate(ListItemType.Footer),
                  DataSource     = dogs
              };
  
              repeater.DataBind();
              PlaceHolder1.Controls.Add(repeater);
          }
  
          // Custom template class to add controls to the repeater's header, item and footer sections.
          private sealed class CustomTemplate : ITemplate
          {
              private ListItemType ListItemType { get; set; }
  
              public CustomTemplate(ListItemType type)
              {
                  ListItemType = type;
              }
  
              public void InstantiateIn(Control container)
              {
                  if (ListItemType == ListItemType.Header)
                  {
                      var table = new LiteralControl();
                      var head = new HtmlGenericControl("thead");
                      var row = new HtmlTableRow();
                      row.Cells.Add(new HtmlTableCell("th") { InnerText = "Breed" });
                      row.Cells.Add(new HtmlTableCell("th") { InnerText = "Name" });
                      head.Controls.Add(row);
                      table.Text = string.Format("&lt;table&gt;{0}&lt;tbody&gt;", head.RenderHtml());
                      container.Controls.Add(table);
                  }
                  else if (ListItemType == ListItemType.Item)
                  {
                      var row = new HtmlTableRow();
  
                      var cell1 = new HtmlTableCell();
                      cell1.Controls.Add(new Literal { ID = "LiteralBreed" });
                      row.Cells.Add(cell1);
  
                      var cell2 = new HtmlTableCell();
                      cell2.Controls.Add(new Literal { ID = "LiteralName" });
                      row.Cells.Add(cell2);
  
                      container.Controls.Add(row);
                      container.DataBinding += new EventHandler(Container_DataBinding);
                  }
                  else if (ListItemType == ListItemType.Footer)
                  {
                      var footer = new LiteralControl("&lt;/tbody&gt;&lt;/table&gt;");
                      container.Controls.Add(footer);
                  }
              }
  
              // Event handler to populate the dog's breed and name in the table when data-binding occurs.
              private void Container_DataBinding(object sender, EventArgs e)
              {
                  var item = sender as RepeaterItem;
                  if (item != null)
                  {
                      var dog = ((Dog)item.DataItem);
  
                      var breed = item.FindDescendantsByType&lt;Literal&gt;().Single(x =&gt; x.ID == "LiteralBreed");
                      breed.Text = dog.Breed;
  
                      var name = item.FindDescendantsByType&lt;Literal&gt;().Single(x =&gt; x.ID == "LiteralName");
                      name.Text = dog.Name;
                  }
              }
          }
  
          private sealed class Dog
          {
              public string Breed { get; set; }
              public string Name { get; set; }
          }
      }
  }
  </code></pre>