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 interface Added a URL expression so you can specify a URL for the content of the <li> element

I wanted to use generated classes as the source so I didn’t feel like implementing the IComposite interface and breaking the code generation (or investing the time to alter the code generation just to implement this custom interface). Therefore, I added the ability to specify an expression that returns a generic list List.

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();
        }
    }