Here, in this article, we're going to extend JS Tree from JS to MVC and we'll render the HTML tags from the database using custom development in the model.
Basically, we'll create a sample DB that contains main/sub categories with self referencing; then we'll add support for Razor like (@HTML.TreeView); and after, we'll render the HTML tags (<ul> <li>) based on our relations in the database.
Let's get started.
Basically, we'll create a sample DB that contains main/sub categories with self referencing; then we'll add support for Razor like (@HTML.TreeView); and after, we'll render the HTML tags (<ul> <li>) based on our relations in the database.
Let's get started.
We will proceed with the following sections.
- Getting Started
- Creating the (Code First) model
- Creating TreeView Component
- Representing Data
Getting Started
In this step, we're going to create the MVC application using Visual Studio 2017 with the following steps.
Step1
Open Visual Studio 2017 and click on “New Project”.

Figure 1 Start Page of VS 2017
Step 2
Select "Web" tab from the left panel and select the “ASP.NET Web Application”.

Figure 2 Creating Web Application in VS 2017
In this step, we're going to create the MVC application using Visual Studio 2017 with the following steps.
Step1
Open Visual Studio 2017 and click on “New Project”.

Figure 1 Start Page of VS 2017
Step 2
Select "Web" tab from the left panel and select the “ASP.NET Web Application”.

Figure 2 Creating Web Application in VS 2017
Step 3
Select “MVC project template” from the ASP.NET Template Wizard to create MVC app.

Figure 3 MVC Project Template
This will create an MVC application. After some changes in default layout page, run the application.

Figure 4 Runing the Project
Creating the (Code First) Model
Now, let's create our Model that handles many to many relationships with self referencing (Categories and unlimited sub categories).
Step 1
Right click on Models folder and select Add->Class and name it (Category).

Figure 5 Adding category model
Right click on Models folder and select Add->Class and name it (Category).

Figure 5 Adding category model
Step 2
Now, Category Model will contain the follwing peroperties.
Id // Category ID
Id // Category ID
Name // Category name
Description //Category description
PID // Parent id
- using System;
- using System.Collections.Generic;
- using System.ComponentModel.DataAnnotations.Schema;
- using System.Linq;
- using System.Web;
- namespace TreeViewMVC.Models
- {
- public class Category
- {
- //Cat Id
- public int ID { get; set; }
- //Cat Name
- public string Name { get; set; }
- //Cat Description
- public string Description { get; set; }
- //represnts Parent ID and it's nullable
- public int? Pid { get; set; }
- [ForeignKey("Pid")]
- public virtual Category Parent { get; set; }
- public virtual ICollection<Category> Childs { get; set; }
- }
- }
Figure 6 Adding model proprties
Step 3 Creating Database context
Right click on the project name from Solution Explorer window and choose Add-> class, and name it "context".

Figure 7 Creating Context
Step 4
Now, we have to install Entity Framework to generate our database.
From the top bar in Visual Studio, click on Tools->Nuget Package Manager -> Manage nuGet packges for sulotion. From the "Browse" tab at the top, select Entity Framwork, then from the right pane, select your project and click on "Install" button.

Figure 8 Installing EntityFramework
Step 3 Creating Database context
Right click on the project name from Solution Explorer window and choose Add-> class, and name it "context".

Figure 7 Creating Context
Step 4
Now, we have to install Entity Framework to generate our database.
From the top bar in Visual Studio, click on Tools->Nuget Package Manager -> Manage nuGet packges for sulotion. From the "Browse" tab at the top, select Entity Framwork, then from the right pane, select your project and click on "Install" button.

Figure 8 Installing EntityFramework
Step 5
Now, we have to include the Model of (Category) that we created recently along with Entity Framework, and map our Category Model.
Now, we have to include the Model of (Category) that we created recently along with Entity Framework, and map our Category Model.
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Web;
- using System.Data.Entity;
- using TreeViewMVC.Models;
- namespace TreeViewMVC
- {
- public class context: DbContext
- {
- public context()
- //Your connection string in Web.Config File
- :base("ConnectionString")
- {
- }
- DbSet<Category> Categories { get; set; }
- }
- }
Step 6
Now, let's create our Database and add migrations to it with some example data in seed. From the top bar in VS, click on Tools -> NuGet Package Manager -> Package Manager console.

Figure 9 Opening Nuget package manager console
Now, let's enable migrations for our Project using Enable-Migrations command.
After excuting this, EF will create a migration in the root of our project, now, we have to add the migration ( let's call it "init").

Now, let's create our Database and add migrations to it with some example data in seed. From the top bar in VS, click on Tools -> NuGet Package Manager -> Package Manager console.

Figure 9 Opening Nuget package manager console
Now, let's enable migrations for our Project using Enable-Migrations command.
After excuting this, EF will create a migration in the root of our project, now, we have to add the migration ( let's call it "init").

Figure 10 Adding init migration
Then, let's add some dummy data into seed. We'll be creating categories and sub categories as the follwing,
- Main Cat1 -> Sub Main Cat1 -> Sub Sub
- Main Cat2
- Main Cat3 -> Sub Main Cat3
- namespace TreeViewMVC.Migrations
- {
- using System;
- using System.Data.Entity;
- using System.Data.Entity.Migrations;
- using System.Linq;
- using TreeViewMVC.Models;
- internal sealed class Configuration : DbMigrationsConfiguration<TreeViewMVC.context>
- {
- public Configuration()
- {
- AutomaticMigrationsEnabled = false;
- }
- //Creating an object from our context class to make the DB operations using EF
- protected override void Seed(TreeViewMVC.context context)
- {
- //Creating Dummy categories and sub categories
- context.Categories.AddOrUpdate(c=>c.Name,
- new Category { ID =1 , Name="Main Cat1" , Pid = null , Description = "Main Cat1" },
- new Category { ID =2 , Name="Sub Main Cat1" , Pid = 1 , Description ="Sub Main Cat1" },
- new Category { ID =3 , Name="Sub Sub" , Pid = 2 , Description ="Sub Sub" },
- new Category { ID =4 , Name="Main Cat2" , Pid = null , Description ="Main Cat2" },
- new Category { ID =5 , Name="Main Cat3" , Pid = null , Description ="Main Cat3" },
- new Category { ID =6 , Name="Sub Main Cat3" , Pid = null , Description ="Sub Main Cat3" }
- );
- }
- }
- }
Go to Tools -> NuGet Package Manager -> Package Manager console and type "Update-Database".
Figure 11 Creating the database from Code First using EFLet's see what we got in the database:

Figure 12 Our Database after executing Update-Database
Creating TreeView class
Step 1
Now, we have to develop the TreeView class "component"
From soulotion explorer window, right click on Models folder ->add ->class and let's name it "Tree"

Figure 13 Adding class "Tree" for creating TreeView component
Step 2
Here we go with our component,
The Tree class will create an HTML tree from a recursive collection of items and handle the (X) model in our case "Category" model and generate <ul> <li> items based on relations we added in the seed.
Here we go with our component,
The Tree class will create an HTML tree from a recursive collection of items and handle the (X) model in our case "Category" model and generate <ul> <li> items based on relations we added in the seed.
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Reflection;
- using System.Web;
- using System.Web.Mvc;
- using System.Web.UI;
- using System.Web.WebPages;
- namespace TreeViewMVC.Models
- {
- public static class TreeViewHelper
- {
- /// <summary>
- /// Create an HTML tree from a recursive collection of items
- /// </summary>
- public static TreeView<T> TreeView<T>(this HtmlHelper html, IEnumerable<T> items)
- {
- return new TreeView<T>(html, items);
- }
- }
- /// <summary>
- /// Create an HTML tree from a resursive collection of items
- /// </summary>
- public class TreeView<T> : IHtmlString
- {
- private readonly HtmlHelper _html;
- private readonly IEnumerable<T> _items = Enumerable.Empty<T>();
- private Func<T, string> _displayProperty = item => item.ToString();
- private Func<T, IEnumerable<T>> _childrenProperty;
- private string _emptyContent = "No children";
- private IDictionary<string, object> _htmlAttributes = new Dictionary<string, object>();
- private IDictionary<string, object> _childHtmlAttributes = new Dictionary<string, object>();
- private Func<T, HelperResult> _itemTemplate;
- public TreeView(HtmlHelper html, IEnumerable<T> items)
- {
- if (html == null) throw new ArgumentNullException("html");
- _html = html;
- _items = items;
- // The ItemTemplate will default to rendering the DisplayProperty
- _itemTemplate = item => new HelperResult(writer => writer.Write(_displayProperty(item)));
- }
- /// <summary>
- /// The property which will display the text rendered for each item
- /// </summary>
- public TreeView<T> ItemText(Func<T, string> selector)
- {
- if (selector == null) throw new ArgumentNullException("selector");
- _displayProperty = selector;
- return this;
- }
- /// <summary>
- /// The template used to render each item in the tree view
- /// </summary>
- public TreeView<T> ItemTemplate(Func<T, HelperResult> itemTemplate)
- {
- if (itemTemplate == null) throw new ArgumentNullException("itemTemplate");
- _itemTemplate = itemTemplate;
- return this;
- }
- /// <summary>
- /// The property which returns the children items
- /// </summary>
- public TreeView<T> Children(Func<T, IEnumerable<T>> selector)
- {
- // if (selector == null) //throw new ArgumentNullException("selector");
- _childrenProperty = selector;
- return this;
- }
- /// <summary>
- /// Content displayed if the list is empty
- /// </summary>
- public TreeView<T> EmptyContent(string emptyContent)
- {
- if (emptyContent == null) throw new ArgumentNullException("emptyContent");
- _emptyContent = emptyContent;
- return this;
- }
- /// <summary>
- /// HTML attributes appended to the root ul node
- /// </summary>
- public TreeView<T> HtmlAttributes(object htmlAttributes)
- {
- HtmlAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
- return this;
- }
- /// <summary>
- /// HTML attributes appended to the root ul node
- /// </summary>
- public TreeView<T> HtmlAttributes(IDictionary<string, object> htmlAttributes)
- {
- if (htmlAttributes == null) throw new ArgumentNullException("htmlAttributes");
- _htmlAttributes = htmlAttributes;
- return this;
- }
- /// <summary>
- /// HTML attributes appended to the children items
- /// </summary>
- public TreeView<T> ChildrenHtmlAttributes(object htmlAttributes)
- {
- ChildrenHtmlAttributes(HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
- return this;
- }
- /// <summary>
- /// HTML attributes appended to the children items
- /// </summary>
- public TreeView<T> ChildrenHtmlAttributes(IDictionary<string, object> htmlAttributes)
- {
- if (htmlAttributes == null) throw new ArgumentNullException("htmlAttributes");
- _childHtmlAttributes = htmlAttributes;
- return this;
- }
- public string ToHtmlString()
- {
- return ToString();
- }
- public void Render()
- {
- var writer = _html.ViewContext.Writer;
- using (var textWriter = new HtmlTextWriter(writer))
- {
- textWriter.Write(ToString());
- }
- }
- private void ValidateSettings()
- {
- if (_childrenProperty == null)
- {
- return;
- }
- }
- public override string ToString()
- {
- ValidateSettings();
- var listItems = new List<T>();
- if (_items != null)
- {
- listItems = _items.ToList();
- }
- var ul = new TagBuilder("ul");
- ul.MergeAttributes(_htmlAttributes);
- var li = new TagBuilder("li")
- {
- InnerHtml = _emptyContent
- };
- li.MergeAttribute("id", "-1");
- if (listItems.Count > 0)
- {
- var innerUl = new TagBuilder("ul");
- innerUl.MergeAttributes(_childHtmlAttributes);
- foreach (var item in listItems)
- {
- BuildNestedTag(innerUl, item, _childrenProperty);
- }
- li.InnerHtml += innerUl.ToString();
- }
- ul.InnerHtml += li.ToString();
- return ul.ToString();
- }
- private void AppendChildren(TagBuilder parentTag, T parentItem, Func<T, IEnumerable<T>> childrenProperty)
- {
- //
- if (childrenProperty == null)
- {
- return;
- }
- var children = childrenProperty(parentItem).ToList();
- if (!children.Any())
- {
- return;
- }
- var innerUl = new TagBuilder("ul");
- innerUl.MergeAttributes(_childHtmlAttributes);
- foreach (var item in children)
- {
- BuildNestedTag(innerUl, item, childrenProperty);
- }
- parentTag.InnerHtml += innerUl.ToString();
- }
- private void BuildNestedTag(TagBuilder parentTag, T parentItem, Func<T, IEnumerable<T>> childrenProperty)
- {
- var li = GetLi(parentItem);
- parentTag.InnerHtml += li.ToString(TagRenderMode.StartTag);
- AppendChildren(li, parentItem, childrenProperty);
- parentTag.InnerHtml += li.InnerHtml + li.ToString(TagRenderMode.EndTag);
- }
- private TagBuilder GetLi(T item)
- {
- var li = new TagBuilder("li")
- {
- InnerHtml = _itemTemplate(item).ToHtmlString()
- };
- Type myType = item.GetType();
- IList<PropertyInfo> props = new List<PropertyInfo>(myType.GetProperties());
- foreach (PropertyInfo prop in props)
- {
- if (prop.Name.ToLower() == "id")
- li.MergeAttribute("id", prop.GetValue(item, null).ToString());
- // Do something with propValue
- if (prop.Name.ToLower() == "sortorder")
- li.MergeAttribute("priority", prop.GetValue(item, null).ToString());
- }
- return li;
- }
- }
- }
Represent Data
Now, we're done with TreeView class and our Model, let's start on View.
Step 1
We have to include jstree, which is jquery plugin, that provides interactive trees.
Now, we're done with TreeView class and our Model, let's start on View.
Step 1
We have to include jstree, which is jquery plugin, that provides interactive trees.
From the top menu, click on Tools -> NuGet Package Manager -> Manage NuGet packages for solution.
Choose "Browse" tab, then type jsTree,
Then, hit on Install button to download JSTree and include it in your project.
Then, hit on Install button to download JSTree and include it in your project.
Step 2
From Solution Explorer window, expand the Views folder and right click on Home folder to add an Empty View, and name it "TreeView".

From Solution Explorer window, expand the Views folder and right click on Home folder to add an Empty View, and name it "TreeView".

Figure 14 Adding an empty view to our project
Now, let's add some code to the our View to display the categories.
- @model IEnumerable<TreeViewMVC.Models.Category>
- @using System.Web.UI.WebControls
- @using TreeViewMVC.Models;
- <h2>TreeView</h2>
- <link href="~/Content/jsTree/themes/default/style.min.css" rel="stylesheet" />
- <div class="form-body">
- <div id="jstree">
- @(Html.TreeView(Model)
- .EmptyContent("root")
- .Children(m => m.Childs)
- .HtmlAttributes(new { id = "tree" })
- .ChildrenHtmlAttributes(new { @class = "subItem" })
- .ItemText(m => m.Name)
- .ItemTemplate(
- @<text>
- <a href="@item.Description" desc="@item.Description">@item.Name</a>
- </text>)
- )
- </div>
- </div>
- @section scripts
- {
- <script src="~/Scripts/jsTree3/jstree.min.js"></script>
- <script>
- $(function () {
- var selectedData;
- $('#jstree').jstree({
- "core": {
- "multiple": true,
- "check_callback": false,
- 'themes': {
- "responsive": true,
- 'variant': 'larg',
- 'stripes': false,
- 'dots': false
- }
- },
- "types": {
- "default": {
- "icon": "fa fa-folder icon-state-warning icon-lg"
- },
- "file": {
- "icon": "fa fa-file icon-state-warning icon-lg"
- }
- },
- "plugins": ["dnd", "state", "types", "sort", "checkbox"]
- });
- });
- </script>
- }
Now, we have to define the View "TreeView" in our Controller "Home controller".
- public ActionResult TreeView()
- {
- var db = new context();
- return View(db.Categories.Where(x => !x.Pid.HasValue).ToList());
- }
Result



RamPosted Nov 23, 2022, 10:38 AM
I have around 7k records in the table, if i using this TreeViewHelper application is very slow. It will take 16 min to take into UI. Could you please help me to get data fast ?
Nik KatPosted Jul 31, 2022, 9:55 PM
This article lacks general explanations on how this multicomponent system works. E.x. what's going on here db.Categories.Where(x => !x.Pid.HasValue).ToList()
Katrina EngelbrechtPosted Dec 11, 2020, 12:30 PM
Hello do you have an example project which builds TreeView with Razor Page instead of MVC? I am looking for an example. Thank you!
QuickWire QuickWirePosted Aug 10, 2019, 9:37 AM
I'm thinking you should have just made an endpoint that generates the JSON data model that JSTree uses. Then use the JSTree data option to fetch it. Am I missing something?
Daniel NguyenPosted Jul 17, 2019, 11:04 AM
How do you make this TreeView to collapse and expand when click? Thank you!
G Krishna RaoPosted May 28, 2019, 6:56 AM
Can i get the same code without entity framework plz.? So that i can save my job.. :(
John BeckwithPosted Sep 20, 2018, 5:46 PM
Hello Jameel, Really great example. Question: I don't want to use codefirst, so to test your example out, I did this in the controller: public ActionResult TreeView () { List<Category> cat = new List<Category> { new Category { ID =1 , Name="Main Cat1" , Pid = null, Description = "Main Cat1" }, etc return View (cat.Where (x => x.Pid.HasValue).ToList()); } but, I get null errors in AppendChildren, which I then fixed but it is not recursing through anymore. Shouldn't the above work?
OemsPosted May 30, 2018, 12:21 PM
Thanks Jammel for sharing. Best regards.
Fauzan FebriPosted Feb 26, 2018, 7:40 PM
Good Sharing. How can I get the ID for selected item on the TreeView? Could you please help me to show it?
ritu cdPosted Jan 13, 2018, 3:19 AM
Public virtual ICollection<Category> Childs { get; set; } in this section
ritu cdPosted Jan 13, 2018, 3:18 AM
What is the role of childs
mike idowuPosted Nov 29, 2017, 5:59 PM
The post only talk about Display what is already inn the database. Please, How do I save, edit into database in ASP.NET MVC Treeview
Manoj SevdaPosted Nov 1, 2017, 5:01 AM
Thank you for sharing
Jaco ZwartsPosted Jul 5, 2017, 6:45 AM
Interesting, thank you for sharing