Musings on code and life

ASP.NET MVC ForEach-Loop Sugar

When you make the move from classic ASP.NET MVC webforms, one of the funny questions you hear is, “Where is the repeater control?”. The MVC veteran responds, “It’s called a For-Loop”. However, if you are used to using the ASP.NET DataList control for doing things like providing repeating columns, then a standard ForEach-Loop won’t do without some help. While I’m sure there are a number of ways to handle this, I want to demonstrate a simple re-usable way to extend what information is available in the foreach loop.

To provide additional information to the loop, I’ve created an extension method that takes an IEnumerable<T> and returns a list of IEnumerable<ForEachItem<T>>. The extra data for the loop is provided in the ForEachItem<T> class.

public class ForEachItem<T> { public T Item { get; set; } public bool First { get; set; } public bool Last { get; set; } public int Index { get; set; } public bool NewGroup { get; set; } }

Here is a list of what each Property represents:

  • Item: This provides access to the original data item
  • First: This boolean is true if it is the first item in the list
  • Last: This boolean is true if it is the last item in the list
  • Index: This is the index of the item in the list
  • NewGroup: This bool represents if this item is the first in a new group (explained below)

The extension method simply iterates through the source list and creates a ForEachItem instance for each item.

public static IEnumerable<ForEachItem<T>> ToFor<T>(this IEnumerable<T> items) { return ToFor(items, 0); } public static IEnumerable<ForEachItem<T>> ToFor<T>(this IEnumerable<T> items, int group) { var list = new List<ForEachItem<T>>(); foreach (var item in items) { var fei = new ForEachItem<T> { First = (list.Count == 0), Item = item, Index = list.Count }; fei.NewGroup = (group > 0 && (list.Count % group == group - 1)); list.Add(fei); } if (list.Count > 0) list[list.Count - 1].Last = true; return list; }

The first extension method ToFor(items) can be used when you don’t require grouping. The second method allows you to provide an integer which will set NewGroup to true for every X items. This can be used to produce things like the RepeatColumns function of the DataList. Here is some example usage to create a table from a loop that has 4 columns across:


<table> <tr> <% foreach (var m in users.ToFor(4)) { %> <td><%= m.Item.Name %></td> <% if (m.NewGroup) app.Write("</tr><tr>"); %> <% } %> </tr> </table>

Another use could be to format a menu of links for a website that need to have a pipe character between the links:

<% foreach (var p in pages.ToFor()) { app.Write("<a href='{0}'>{1}</a>", p.Item.Href, p.Item.Title); if (!p.Last) app.Write("&nbsp;&nbsp;|&nbsp;&nbsp;"); } %>

If you find this useful, feel free to leave a comment and let me know. I’d also love to hear if you have other techniques that you use to solve the same sorts of problems.