Update 2/10/2009: I have updated the helper code a little bit to include an ID parameter as well as to inject the jQuery treeview script code automatically. Please modify to your desire, or make sure to include the jQuery TreeView plugin script to your page before running.

The following helper will make it easy to create a tree view from a recursive self-referencing table. Below you are seeing a tree of “Locations” where each Location can contain X number of child locations.

Dependencies

 jQuery TreeView Plugin


Rendered Tree

image

Table Definition

The table itself is extremely simple, each Location has a ParentLocationId which is a relationship to the same table. If the ParentLocationId is null then it is a root location.

 image image


Usage

Simple

<%= Html.TreeView("locations", 
    Model.Locations, 
    l => l.ChildrenLocations, 
    l => l.Name) %>

Wrapping a div around each list item

<%= Html.TreeView("dropTree", 
    Model.Locations, 
    l => l.ChildrenLocations, 
    l => "<div class='dropZone'>" + l.Name + "<div>")

Making each list item an ActionLink

<%= Html.TreeView("dropTree", 
    Model.Locations, 
    l => l.ChildrenLocations, 
    l => Html.ActionLink("MyController", "MyAction", l.Name, new { id = l.Name })

The Code

public static class TreeViewHtmlHelper
{
    /// <summary>
    /// Create a TreeView of nodes starting from a root element
    /// </summary>
    /// <param name="treeId">The ID that will be used when the ul is created</param>
    /// <param name="rootItems">The root nodes to create</param>
    /// <param name="childrenProperty">A lambda expression that returns the children nodes</param>
    /// <param name="itemContent">A lambda expression defining the content in each tree node</param>
    public static string TreeView<T>(this HtmlHelper html, string treeId, IEnumerable<T> rootItems, Func<T, IEnumerable<T>> childrenProperty, Func<T, string> itemContent)
    {
        return html.TreeView(treeId, rootItems, childrenProperty, itemContent, true, null);
    }

    /// <summary>
    /// Create a TreeView of nodes starting from a root element
    /// </summary>
    /// <param name="treeId">The ID that will be used when the ul is created</param>
    /// <param name="rootItems">The root nodes to create</param>
    /// <param name="childrenProperty">A lambda expression that returns the children nodes</param>
    /// <param name="itemContent">A lambda expression defining the content in each tree node</param>
    /// <param name="includeJavaScript">If true, output will automatically render the JavaScript to turn the ul into the treeview</param>    
    public static string TreeView<T>(this HtmlHelper html, string treeId, IEnumerable<T> rootItems, Func<T, IEnumerable<T>> childrenProperty, Func<T, string> itemContent, bool includeJavaScript)
    {
        return html.TreeView(treeId, rootItems, childrenProperty, itemContent, includeJavaScript, null);
    }

    /// <summary>
    /// Create a TreeView of nodes starting from a root element
    /// </summary>
    /// <param name="treeId">The ID that will be used when the ul is created</param>
    /// <param name="rootItems">The root nodes to create</param>
    /// <param name="childrenProperty">A lambda expression that returns the children nodes</param>
    /// <param name="itemContent">A lambda expression defining the content in each tree node</param>
    /// <param name="includeJavaScript">If true, output will automatically render the JavaScript to turn the ul into the treeview</param>
    /// <param name="emptyContent">Content to be rendered when the tree is empty</param>
    /// <param name="includeJavaScript">If true, output will automatically into the JavaScript to turn the ul into the treeview</param>    
    public static string TreeView<T>(this HtmlHelper html, string treeId, IEnumerable<T> rootItems, Func<T, IEnumerable<T>> childrenProperty, Func<T, string> itemContent, bool includeJavaScript, string emptyContent)
    {
        StringBuilder sb = new StringBuilder();

        sb.AppendFormat("<ul id='{0}'>\r\n", treeId);

        if(rootItems.Count() == 0)
        {
            sb.AppendFormat("<li>{0}</li>", emptyContent);
        }

        foreach (T item in rootItems)
        {
            RenderLi(sb, item, itemContent);
            AppendChildren(sb, item, childrenProperty, itemContent);
        }

        sb.AppendLine("</ul>");

        if (includeJavaScript)
        {
            sb.AppendFormat(
                @"<script type='text/javascript'>
                    $(document).ready(function() {{
                        $('#{0}').treeview({{ animated: 'fast' }});
                    }});
                </script>", treeId);
        }

        return sb.ToString();
    }

    private static void AppendChildren<T>(StringBuilder sb, T root, Func<T, IEnumerable<T>> childrenProperty, Func<T, string> itemContent)
    {
        var children = childrenProperty(root);
        if(children.Count() == 0)
        {
            sb.AppendLine("</li>");
            return;
        }

        sb.AppendLine("\r\n<ul>");
        foreach (T item in children)
        {
            RenderLi(sb, item, itemContent);
            AppendChildren(sb, item, childrenProperty, itemContent);
        }

        sb.AppendLine("</ul></li>");
    }

    private static void RenderLi<T>(StringBuilder sb, T item, Func<T, string> itemContent)
    {
        sb.AppendFormat("<li>{0}", itemContent(item));            
    }
}

Technorati Tags: ,

Comments

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
bhupendran
2/20/2009 2:43 AM
Hi,

Ur post seems very good. Can u tell me what i have to do if i want to give links to all my treeview items.

waiting for ur reply

regards
bhupendran.m

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
mhidinger
2/20/2009 9:30 AM
Hi bhupendran,

It is actually quite simple to put ActionLinks within your treeview items. The following code should take care of it for you:

<%= Html.TreeView("locations",
Model.Locations,
l => l.ChildrenLocations,
l => Html.ActionLink("MyController", "MyAction", l.Name) %>

Of course, if you want to pass the current item as a parameter to the action, you can use the anonymous dictionary syntax, as follows:

<%= Html.TreeView("locations",
Model.Locations,
l => l.ChildrenLocations,
l => Html.ActionLink("MyController", "MyAction", l.Name, new { id = l.Name }) %>

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
Mr zhan
3/2/2009 2:58 AM
很好!
我正在学习中!
觉得不错!

# Interesting Finds: 2009-03-08

Gravatar
Bolik
3/7/2009 8:41 PM
SpiffUpYourASP.NETMVCFormsWithjQueryChapter6-UnderstandingHTMLHelpersASP.NETMVCRecu...

# Interesting Finds: 2009-03-08

Gravatar
Bolik
3/7/2009 8:41 PM
Spiff Up Your ASP.NET MVC Forms With jQuery Chapter 6 - Understanding HTML Helpers ASP.NET MVC Recursive

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
Uma
3/12/2009 11:24 AM
Matt,

Can you share the code in your model? I could not figure out how you are getting Model.Locations and
l => l.ChildrenLocations!

As I do not have a table, I had to build an object as the same structure you have for table. I am able to construct the object with proper hierarchy. Here is my class

public class TreeNode
{
public TreeNode(){}
public string NodeId { get; set; }
public string ParentId { get; set; }
public string Name { get; set; }
}

Now I need to knwo how do I derive

Model.Locations and
l => l.ChildrenLocations!

from List<TreeNode>();

Appreciate your reply
-Uma

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
Tim
3/23/2009 10:31 AM
Any update on how to get the model for this?

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
Tim
3/23/2009 10:48 AM
Any update on how to get the model for this?

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
mhidinger
3/23/2009 5:29 PM
Sorry for the delayed response I am still settling back in from vacation.

The model actually only requires a self-referencing IEnumerable of itself.

Using Uma's example above,

public class TreeNode
{
public TreeNode(){}
public string NodeId { get; set; }
public string ParentId { get; set; }
public string Name { get; set; }
public IEnumerable<TreeNode> Children { get; }
}

You need to add that Children property, which is just an IEnumerable<TreeNode>

In my Locations example above, I have the following (simplified) structure:

public class Location
{
public IEnumerable<Location> ChildrenLocations { get; }
}


To use Uma's TreeNode, you would simply:

<%= Html.TreeView("treeNodes",
Model.TreeNodes,
node => node.Children,
node => node.Name) %>

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
Pandurang
5/14/2009 11:21 PM
Hi,

Can you please explain how to define "Locations" object (w.r.t. Model.TreeNodes in declaration in Viewpage) in model? What kind of object to be passes to Viewpage from controller class?
Will it be ok if we pass List<TreeNode> as rootnode and childrens?

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
lee
5/18/2009 5:41 AM
why are you didn't post the surce code.
i got problem lambda expretion.

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
lee
5/19/2009 3:11 AM
i am so curious about you pass parameter to view

on the assumption that
public class Location
{
public IEnumerable<Location> ChildrenLocations { get; }
}


how you can pass the parameter to the view
and you fill up the parameter to the as follows

<%= Html.TreeView("locations", Model.Locations, l => l.ChildrenLocations, l => l.Name) %>

so i am try to pass the prametes to the me ,but it's failure.

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Barefoot.Crawler.Web.Models.TreeNode>"%>

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
lee
5/19/2009 3:12 AM
i am so curious about you pass parameter to view

on the assumption that
public class Location
{
public IEnumerable<Location> ChildrenLocations { get; }
}


how you can pass the parameter to the view
and you fill up the parameter to the as follows

<%= Html.TreeView("locations", Model.Locations, l => l.ChildrenLocations, l => l.Name) %>

so i am try to pass the prametes to the me ,but it's failure.

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Barefoot.Crawler.Web.Models.TreeNode>"%>

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
lee
5/19/2009 3:31 AM
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Barefoot.Crawler.Web.Models.TreeNode>"%>


what's type you pass to the view ,i am very very curiouse about it. please contact me as much as possible/

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
Tom
6/18/2009 4:46 AM
Hi
It'd be possible to share entire project? I have big difficulties to use it.

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
matma
6/19/2009 7:17 AM
Hi,
Your code is very helpful for me, but i've found two bugs - one when child property is null, and another, much bigger, when rootItems weren't IEnumerable then compilator wasn't happy ;).

Could You look at these changes, and maybe update the code ;)
private static void AppendChildren<T>( StringBuilder sb, T root, Func<T, IEnumerable<T>> childrenProperty, Func<T, string> itemContent )
{
var children = childrenProperty( root );
//here should check if not null
if ( children == null || children.Count() == 0 )
{
sb.AppendLine( "</li>" );
return;
}


//When change to T rootItem we have two possible ways - one when T is IEnumerable, and one when isn't

public static string TreeView<T>( this HtmlHelper html, string treeId, T rootItem, Func<T, IEnumerable<T>> childrenProperty, Func<T, string> itemContent, bool includeJavaScript, string emptyContent )
{
StringBuilder sb = new StringBuilder();

sb.AppendFormat( "<ul id='{0}'>\r\n", treeId );

if ( rootItem is IEnumerable<T> )
{
if ( ( rootItem as IEnumerable<T> ).Count() == 0 )
{
sb.AppendFormat( "<li>{0}</li>", emptyContent );
}

foreach ( T item in rootItem as IEnumerable<T> )
{
RenderLi( sb, item, itemContent );
AppendChildren( sb, item, childrenProperty, itemContent );
}
}
else
{
RenderLi( sb, rootItem, itemContent );
AppendChildren( sb, rootItem, childrenProperty, itemContent );
}
sb.AppendLine( "</ul>" );



Regards
Mateusz

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
于兵建
7/4/2009 9:29 AM
些好来~~~~great~~~

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
cici
7/28/2009 9:15 PM
i am so curious about you pass parameter to view

on the assumption that
public class Location
{
public IEnumerable<Location> ChildrenLocations { get; }
}


how you can pass the parameter to the view
and you fill up the parameter to the as follows

<%= Html.TreeView("locations", Model.Locations, l => l.ChildrenLocations, l => l.Name) %>

so i am try to pass the prametes to the me ,but it's failure.

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<Barefoot.Crawler.Web.Models.TreeNode>"%>

this is lee's question,i aslo want to know the answer

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
Martin
8/7/2009 7:56 AM
Could you show the SQL or LINQ used in this example ?

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
HaiLee
8/13/2009 3:14 AM
Your code is very helpful for me! Thank!谢谢!

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
John Haines
8/19/2009 2:30 PM
I have the treeview working except for the fact that it doesn't display the plus and minus incons or the lines between them.

Any suggestions?

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
Michal
9/5/2009 3:00 PM
Could you show the Controller, Views and Model used in this example ?

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
NqyNik
9/11/2009 9:39 AM
How do you pass the data in using the entity framework?

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
Endy
11/28/2009 4:16 AM
I'm a MVC beginner,couldn't you send the source code for me by email? THX.

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
Endy
11/28/2009 4:24 AM
I'm a MVC beginner,couldn't you send the source code for me by email?

# Great Job!

Gravatar
Jason
12/17/2009 2:58 AM
Great Job!It's really help!

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
Daniel
12/23/2009 3:12 PM
Hello Matt.
Can you send me a the project of this example?
If you have a sample were children load by Ajax, it would be great.
Thanks in advance

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
Daniel
12/23/2009 3:12 PM
Hello Matt.
Can you send me a the project of this example?
If you have a sample were children load by Ajax, it would be great.
Thanks in advance

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
gridare "Bingo!"
12/25/2009 4:57 AM
The solution, as you have guessed from the title, is to build your own validation control when possible to promote re usability.ASP.NET validations are ASP.NET controls that when associated with a web control will perform client-side and server-side validation.

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
Dinesh Kumar
1/1/2010 3:06 AM
I am not able to find Model.Locations in the following code:-( . its showing me the following error=>

'object' does not contain a definition for 'Locations' and no extension method 'Locations' accepting a first argument of type 'object' could be found (are you missing a using directive or an assembly reference?)

<%= Html.TreeView("locations",
Model.Locations,
l => l.ChildrenLocations,
l => l.Name) %>

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
marius
1/11/2010 3:04 AM
It worked just fine...
Thx!

<%= Html.TreeView("categories", repository.Categories, l => l.Categories, l => Html.ActionLink<ProductController>(p=>p.List(l.Name,null,null),l.Name).ToHtmlString(), false, "No categories found.")%>

# re: ASP.NET MVC Recursive TreeView Helper

Gravatar
非非
1/29/2010 1:31 AM
非常不错,感谢。。。
Comments have been closed on this topic.