<p>This is what I've got.  It works.  But, is there a simpler or better way?</p>

<p>ASPX Page&hellip;</p>

<pre><code>&lt;asp:Repeater ID="RepeaterBooks" runat="server"&gt;
    &lt;HeaderTemplate&gt;
        &lt;table class="report"&gt;
            &lt;tr&gt;
                &lt;th&gt;Published&lt;/th&gt;
                &lt;th&gt;Title&lt;/th&gt;
                &lt;th&gt;Author&lt;/th&gt;
                &lt;th&gt;Price&lt;/th&gt;
            &lt;/tr&gt;
    &lt;/HeaderTemplate&gt;
    &lt;ItemTemplate&gt;
            &lt;tr&gt;
                &lt;td&gt;&lt;asp:Literal ID="LiteralPublished" runat="server" /&gt;&lt;/td&gt;
                &lt;td&gt;&lt;asp:Literal ID="LiteralTitle" runat="server" /&gt;&lt;/td&gt;
                &lt;td&gt;&lt;asp:Literal ID="LiteralAuthor" runat="server" /&gt;&lt;/td&gt;
                &lt;td&gt;&lt;asp:Literal ID="LiteralPrice" runat="server" /&gt;&lt;/td&gt;
            &lt;/tr&gt;
    &lt;/ItemTemplate&gt;
    &lt;FooterTemplate&gt;
        &lt;/table&gt;
    &lt;/FooterTemplate&gt;
&lt;/asp:Repeater&gt;
</code></pre>

<p>ASPX.VB Code Behind&hellip;</p>

<pre><code>Protected Sub Page_Load( ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
    Dim db As New BookstoreDataContext
    RepeaterBooks.DataSource = From b In db.Books _
                               Order By b.Published _
                               Select b
    RepeaterBooks.DataBind()
End Sub

Sub RepeaterBooks_ItemDataBound( ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles RepeaterBooks.ItemDataBound
    If e.Item.ItemType = ListItemType.Item Or e.Item.ItemType = ListItemType.AlternatingItem Then
        Dim b As Book = DirectCast(e.Item.DataItem, Book)
        DirectCast(e.Item.FindControl("LiteralPublished"), Literal).Text = "&lt;nobr&gt;" + b.Published.ToShortDateString + "&lt;/nobr&gt;"
        DirectCast(e.Item.FindControl("LiteralTitle"), Literal).Text = "&lt;nobr&gt;" + TryNbsp(HttpContext.Current.Server.HtmlEncode(b.Title)) + "&lt;/nobr&gt;"
        DirectCast(e.Item.FindControl("LiteralAuthor"), Literal).Text = "&lt;nobr&gt;" + TryNbsp(HttpContext.Current.Server.HtmlEncode(b.Author)) + "&lt;/nobr&gt;"
        DirectCast(e.Item.FindControl("LiteralPrice"), Literal).Text = "&lt;nobr&gt;" + Format(b.Price, "c") + "&lt;/nobr&gt;"
    End If
End Sub

Function TryNbsp(ByVal s As String) As String
    If s = "" Then
        Return "&amp;nbsp;"
    Else
        Return s
    End If
End Function
</code></pre>

[{'answer_id': 43818, 'author': 'Geoff', 'author_id': 1097, 'author_profile': 'https://Stackoverflow.com/users/1097', 'pm_score': 2, 'selected': False, 'text': '<p>In .Net 3.0+ you can replace your ItemDataBound to the asp:Literal by doing something like this:</p>\n\n<pre><code>&lt;ItemTemplate&gt;\n            &lt;tr&gt;\n                &lt;td&gt;&lt;%# Eval("published") %&gt;&lt;/td&gt;\n                ...\n</code></pre>\n\n<p>where "published" is the name of a field in the data you have bound to the repeater</p>\n\n<p>Edit:\n@<a href="https://stackoverflow.com/questions/43803/how-do-i-best-populate-an-html-table-in-aspnet#43894">Alassek</a>: I think the performance hit of reflection is often over-emphasized.  Obviously you need to benchmark performance of your app, but the hit of the Eval is likely measured in milliseconds.  Unless your app is serving many concurrent hits, this probably isn\'t an issue, and the simplicity of the code using Eval, along with it being a good separation of the presentation, make it a good solution.</p>\n'}, {'answer_id': 43820, 'author': 'mattruma', 'author_id': 1768, 'author_profile': 'https://Stackoverflow.com/users/1768', 'pm_score': 1, 'selected': False, 'text': '<p>I agree with Geoff, the only time we use <code>Literals</code> is if we want to do something <strong>different</strong> with the data.<br>\nFor example, we might want a <code>DueDate</code> field to say "Today" or "Yesterday" instead of the actual date.</p>\n'}, {'answer_id': 43894, 'author': 'Adam Lassek', 'author_id': 1249, 'author_profile': 'https://Stackoverflow.com/users/1249', 'pm_score': 3, 'selected': True, 'text': '<p>@Geoff</p>\n\n<p>That sort of Eval statement was actually added in 2.0, but if performance is important Eval should be avoided since it uses Reflection.</p>\n\n<p>The repeater is a pretty good way of doing it, although it might be faster to generate the table in code:</p>\n\n<p>ASPX Page:</p>\n\n<pre><code>&lt;table class="report" id="bookTable" runat="server"&gt;\n        &lt;tr&gt;\n            &lt;th&gt;Published&lt;/th&gt;\n            &lt;th&gt;Title&lt;/th&gt;\n            &lt;th&gt;Author&lt;/th&gt;\n            &lt;th&gt;Price&lt;/th&gt;\n        &lt;/tr&gt;\n &lt;/table&gt;\n</code></pre>\n\n<p>Code Behind:</p>\n\n<pre><code>Protected Sub Page_Load( ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load\n    If Not Page.IsPostback Then\n        BuildTable()\n    End If\nEnd Sub\n\nPrivate Sub BuildTable()\n    Dim db As New BookstoreDataContext\n    Dim bookCollection = from b in db.Books _\n                         Order By b.Published _\n                         Select b\n    Dim row As HtmlTableRow\n    Dim cell As HtmlTableCell\n\n    For Each book As Books In bookCollection\n        row = New HtmlTableRow()\n        cell = New HtmlTableCell With { .InnerText = b.Published.ToShortDateString }\n        row.Controls.Add(cell)\n        cell = New HtmlTableCell With { .InnerText = TryNbsp(HttpContext.Current.Server.HtmlEncode(b.Title)) }\n        row.Controls.Add(cell)\n        cell = New HtmlTableCell With { .InnerText = TryNbsp(HttpContext.Current.Server.HtmlEncode(b.Author))\n        row.Controls.Add(cell)\n        cell = New HtmlTableCell With { .InnerText = Format(b.Price, "c") }\n        row.Controls.Add(cell)\n        bookTable.Controls.Add(row)\n    Next\n</code></pre>\n\n<p>I guess it depends on how important speed is to you. For simplicity\'s sake I think I would go with the Repeater.</p>\n'}, {'answer_id': 43922, 'author': 'Zack Peterson', 'author_id': 83, 'author_profile': 'https://Stackoverflow.com/users/83', 'pm_score': 0, 'selected': False, 'text': '<blockquote>\n  <p><strong><a href="https://stackoverflow.com/users/1249/alassek">ALassek</a></strong> wrote:</p>\n  \n  <p>&hellip;generate the table in code&hellip;</p>\n</blockquote>\n\n<p>I like the look of that!  It seems MUCH less likely to produce a run-time exception due to a typo or field name change.</p>\n'}, {'answer_id': 43932, 'author': 'Serhat Ozgel', 'author_id': 31505, 'author_profile': 'https://Stackoverflow.com/users/31505', 'pm_score': 2, 'selected': False, 'text': '<p>The <a href="http://msdn.microsoft.com/en-us/library/bb398790.aspx" rel="nofollow noreferrer">ListView</a> control introduced with framework 3.5 might be a little bit better solution. Your markup would look like this:</p>\n<pre class="lang-xml prettyprint-override"><code>&lt;asp:ListView runat=&quot;server&quot; ID=&quot;ListView1&quot;\n    DataSourceID=&quot;SqlDataSource1&quot;&gt;\n  &lt;LayoutTemplate&gt;\n    &lt;table runat=&quot;server&quot; id=&quot;table1&quot; runat=&quot;server&quot; &gt;\n      &lt;tr runat=&quot;server&quot; id=&quot;itemPlaceholder&quot; &gt;&lt;/tr&gt;\n    &lt;/table&gt;\n  &lt;/LayoutTemplate&gt;\n  &lt;ItemTemplate&gt;\n    &lt;tr runat=&quot;server&quot;&gt;\n      &lt;td runat=&quot;server&quot;&gt;\n        &lt;asp:Label ID=&quot;NameLabel&quot; runat=&quot;server&quot;\n          Text=\'&lt;%#Eval(&quot;Name&quot;) %&gt;\' /&gt;\n      &lt;/td&gt;\n    &lt;/tr&gt;\n  &lt;/ItemTemplate&gt;\n&lt;/asp:ListView&gt;\n</code></pre>\n<p>You\'ll want to set your data source ID from a public or private property in the code-behind class.</p>\n'}, {'answer_id': 44051, 'author': 'Mark Cidade', 'author_id': 1659, 'author_profile': 'https://Stackoverflow.com/users/1659', 'pm_score': 1, 'selected': False, 'text': '<p>This is what the GridView is for.</p>\n\n<pre><code>&lt;asp:GridView runat="server" DataSourceID="SqlDataSource1"&gt;\n   &lt;Columns&gt;\n      &lt;asp:BoundField HeaderText="Published" DataField="Published" /&gt;\n      &lt;asp:BoundField HeaderText="Author" DataField="Author" /&gt;\n   &lt;/Columns&gt;\n&lt;/asp:GridView&gt;\n</code></pre>\n'}, {'answer_id': 44081, 'author': 'EndangeredMassa', 'author_id': 106, 'author_profile': 'https://Stackoverflow.com/users/106', 'pm_score': 1, 'selected': False, 'text': '<p>I would use a GridView (or DataGrid, if you are using an older version of ASP.NET).</p>\n\n<pre><code>&lt;asp:GridView ID="gvBooks" runat="server" AutoGenerateColumns="False"&gt;\n    &lt;Columns&gt;\n        &lt;asp:BoundField HeaderText="Published" DataField="Published" /&gt;\n        &lt;asp:BoundField HeaderText="Title" DataField="Title" /&gt;                     \n        &lt;asp:BoundField HeaderText="Author" DataField="Author" /&gt;\n        &lt;asp:BoundField HeaderText="Price" DataField="Price" /&gt;\n    &lt;/Columns&gt;\n&lt;/asp:GridView&gt;\n</code></pre>\n\n<p>With some code-behind:</p>\n\n<pre><code>Private Sub gvBooksRowDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.GridViewRowEventArgs) Handles gvBooks.RowDataBound\n     Select Case e.Row.RowType\n        Case DataControlRowType.DataRow\n\n            \'\'\' Your code here \'\'\'\n\n     End Select\nEnd Sub\n</code></pre>\n\n<p>You can bind it in a similar way. The RowDataBound event is what you need.</p>\n'}, {'answer_id': 44091, 'author': 'mattruma', 'author_id': 1768, 'author_profile': 'https://Stackoverflow.com/users/1768', 'pm_score': 0, 'selected': False, 'text': "<p>If you don't need ASP.NET handled edit cabilities I would <strong>stay away</strong> from the DataGrid and the GridView ... they provide <strong>unnecessary bloat</strong>.</p>\n"}]
['https://Stackoverflow.com/questions/43803', 'https://Stackoverflow.com', 'https://Stackoverflow.com/users/83/']