Rendering a tree view using ASP.NET MVC 3 + Razor
I modified the Html Helper created by Mike Hadlow based on the answered provided by marcind over on Stack Overflow and it works pretty beautifully.
I also made a couple “improvements”:
Updated to use the HtmlTextWriter provided by the HtmlHelper so that it is compatible with MVC 3.
Added a children expression so you don’t need the IComposite
I wanted to use generated classes as the source so I didn’t feel like implementing the IComposite
Here is what the updated Razor syntax looks like:
<div id="treeView">
@Html.RenderTree(Model.Divisions, division => division.Name, division => division.Children.ToList(), division => "../../Division/Detail/" + division.Id)
</div>
Here is my updated extension method:
public static class TreeRenderHtmlHelper
{
public static string RenderTree<T>(this HtmlHelper htmlHelper, List<T> rootLocations, Func<T, string> locationRenderer, Func<T, List<T>> childrenRenderer, Func<T, string> urlRenderer)
{
return new TreeRenderer<T>(rootLocations, locationRenderer, childrenRenderer, urlRenderer).Render(new HtmlTextWriter(htmlHelper.ViewContext.Writer));
}
}
Here is my updated TreeRenderer
public class TreeRenderer<T>
{
private readonly Func<T, string> locationRenderer;
private readonly Func<T, string> urlRenderer;
private readonly Func<T, List<T>> childrenRenderer;
private readonly List<T> rootLocations;
private HtmlTextWriter writer;
public TreeRenderer(List<T> rootLocations, Func<T, string> locationRenderer, Func<T, List<T>> childrenRenderer, Func<T, string> urlRenderer)
{
this.rootLocations = rootLocations;
this.locationRenderer = locationRenderer;
this.childrenRenderer = childrenRenderer;
this.urlRenderer = urlRenderer;
}
public string Render(HtmlTextWriter writer)
{
this.writer = writer;
RenderLocations(rootLocations);
//var mvcString = new HtmlString();
return string.Empty;
}
/// <summary>
/// Recursively walks the location tree outputting it as hierarchical UL/LI elements
/// </summary>
/// <param name="locations"></param>
private void RenderLocations(List<T> locations)
{
if (locations == null) return;
if (locations.Count() == 0) return;
InUl(() => locations.ForEach(location => InLi(() =>
{
writer.AddAttribute("href", urlRenderer(location));
writer.RenderBeginTag(HtmlTextWriterTag.A);
writer.Write(locationRenderer(location));
writer.RenderEndTag();
var children = this.childrenRenderer(location);
RenderLocations(children);
})));
}
private void InUl(Action action)
{
writer.WriteLine();
writer.RenderBeginTag(HtmlTextWriterTag.Ul);
action();
writer.RenderEndTag();
writer.WriteLine();
}
private void InLi(Action action)
{
writer.RenderBeginTag(HtmlTextWriterTag.Li);
action();
writer.RenderEndTag();
writer.WriteLine();
}
}