I was hoping that these were built already but couldn’t see any descent results with a quick Google search so figured I’d just write my own… these are now part of the Umbraco v5 codebase. Here’s 2 HtmlHelper methods that allow you to very quickly create an Html table output based on an IEnumerable data structure. Luckily for us, Phil Haack showed us what Templated Razor Delegates are which makes stuff like this very elegant.

There are 2 types of tables: a table that expands vertically (you set a maximum number of columns), or a table that expands horizontally (you set a maximum amount of rows). So I’ve created 2 HtmlHelper extensions for this:

Html.TableHorizontal Html.TableVertical

Say you have a partial view with a declaration of something like:

@model IEnumerable<MyDataObject>

To render a table is just one call and you can customize exactly how each cell is rendered and styled using Razor delegates, in the following example the MyDataObject class contains 3 properties: Name, Enabled and Icon and each cell is going to render a check box with a CSS image as it’s label and there will be a maximum of 8 columns:

@Html.TableVertical(Model, 8, @<text> @Html.CheckBox(@item.Name, @item.Enabled) <label for="@Html.Id(@item.Name)" class="@item.Icon"> <span>@item.Name</span> </label> </text>)

Nice! that’s it, far too easy. One thing to note is the @<text> </text> syntax as this is special Razor parser syntax that declares a Razor delegate without having to render additional html structures. Generally Razor delegates will require some Html tags such as this: @<div> </div> but if you don’t require surrounding Html structures, you can use the special @<text> </text> tags.

Lastly here’s all the HtmlHelper code… I’ve created these extensions as HtmlHelper extensions only for consistency but as you’ll be able to see, the HtmlHelper is never actually used and these extensions could simply be extensions of IEnumerable<T>.

/// <summary> /// Creates an Html table based on the collection /// which has a maximum numer of rows (expands horizontally) /// </summary> public static HelperResult TableHorizontal<T>(this HtmlHelper html, IEnumerable<T> collection, int maxRows, Func<T, HelperResult> template) where T : class { return new HelperResult(writer => { var items = collection.ToArray(); var itemCount = items.Count(); var maxCols = Convert.ToInt32(Math.Ceiling(items.Count() / Convert.ToDecimal(maxRows))); //construct a grid first var grid = new T[maxCols, maxRows]; var current = 0; for (var x = 0; x < maxCols; x++) for (var y = 0; y < maxRows; y++) if (current < itemCount) grid[x, y] = items[current++]; WriteTable(grid, writer, maxRows, maxCols, template); }); } /// <summary> /// Creates an Html table based on the collection /// which has a maximum number of cols (expands vertically) /// </summary> public static HelperResult TableVertical<T>(this HtmlHelper html, IEnumerable<T> collection, int maxCols, Func<T, HelperResult> template) where T : class { return new HelperResult(writer => { var items = collection.ToArray(); var itemCount = items.Count(); var maxRows = Convert.ToInt32(Math.Ceiling(items.Count() / Convert.ToDecimal(maxCols))); //construct a grid first var grid = new T[maxCols, maxRows]; var current = 0; for (var y = 0; y < maxRows; y++) for (var x = 0; x < maxCols; x++) if (current < itemCount) grid[x, y] = items[current++]; WriteTable(grid, writer, maxRows, maxCols, template); }); } /// <summary> /// Writes the table markup to the writer based on the item /// input and the pre-determined grid of items /// </summary> private static void WriteTable<T>(T[,] grid, TextWriter writer, int maxRows, int maxCols, Func<T, HelperResult> template) where T : class { //create a table based on the grid writer.Write("<table>"); for (var y = 0; y < maxRows; y++) { writer.Write("<tr>"); for (var x = 0; x < maxCols; x++) { writer.Write("<td>"); var item = grid[x, y]; if (item != null) { //if there's an item at that grid location, call its template template(item).WriteTo(writer); } writer.Write("</td>"); } writer.Write("</tr>"); } writer.Write("</table>"); }