In this article, we will create dynamic treeview in asp.net mvc from database data, which will help you link parent node with the child node and sub-child node easily. We will create a dynamic tree view menu fetched from the database, using ASP.NET MVC 5, C#, Razor and SQL Server 2012 with the help of JStree jquery plugin.
Step 1: Create a project in your Visual Studio(2017 in my example), by opening Visual Studio and clicking "File"-> "New"-> "Project"
Select MVC template to generate basic home controller and other details
Step 2: Now, for example ,I have this table in my database, which we need to show in Treeview, with the following table rows
as you can see above we have four columns on the table, where Pid is Main parent Id, you can create this table in your database with the following script
USE [TreeviewMVC]
GO
/****** Object: Table [dbo].[Categories] Script Date: 12/31/2017 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
SET ANSI_PADDING ON
GO
CREATE TABLE [dbo].[Categories](
[ID] [int] IDENTITY(1,1) NOT NULL,
[Name] [varchar](100) NULL,
[Description] [varchar](100) NULL,
[Pid] [int] NULL,
CONSTRAINT [PK_Categories] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
SET IDENTITY_INSERT [dbo].[Categories] ON
INSERT [dbo].[Categories] ([ID], [Name], [Description], [Pid]) VALUES (1, N'Java', N'Java', NULL)
INSERT [dbo].[Categories] ([ID], [Name], [Description], [Pid]) VALUES (2, N'JSP', N'JSP', 1)
INSERT [dbo].[Categories] ([ID], [Name], [Description], [Pid]) VALUES (3, N'ASP.NET', N'ASP.NET', NULL)
INSERT [dbo].[Categories] ([ID], [Name], [Description], [Pid]) VALUES (4, N'ASP.NET MVC', N'ASP.NET MVC', 3)
INSERT [dbo].[Categories] ([ID], [Name], [Description], [Pid]) VALUES (5, N'Web-Forms', N'Web-Forms', 3)
INSERT [dbo].[Categories] ([ID], [Name], [Description], [Pid]) VALUES (6, N'Razor Syntax', N'Razor Syntax', 4)
SET IDENTITY_INSERT [dbo].[Categories] OFF
/****** Object: Index [IX_Pid] Script Date: 12/31/2017 6:39:22 PM ******/
CREATE NONCLUSTERED INDEX [IX_Pid] ON [dbo].[Categories]
(
[Pid] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, DROP_EXISTING = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
GO
ALTER TABLE [dbo].[Categories] WITH CHECK ADD CONSTRAINT [FK_dbo.Categories_dbo.Categories_Pid] FOREIGN KEY([Pid])
REFERENCES [dbo].[Categories] ([ID])
GO
ALTER TABLE [dbo].[Categories] CHECK CONSTRAINT [FK_dbo.Categories_dbo.Categories_Pid]
GO
Step 3: Now link project with database using ADO.NET and Entity framework, right click on Models folder in Visual Studio , Select "Add" -> "ADO.NET Entity Model"/ "New Item"->"Data(Left pane) & ADO.NET Entity Model(right pane)" -> Name it and click "Add"
Select "Ef designer from database" -> Click "Next"-> "New Connection" -> Conenct to your database by entering credentials of your database and selecting database
After entering connecting to database and clicking "OK", Select "Yes, include the sensitive data in the connection string", click "Next", Select Ef version "Entity Framework 6.0", Click "Next", check "Tables" and click "Finish"
Step 4: Now let's create a custom HTML helper class to render treeview in Razor, Add new class in "Models" folder and name it as "TreeViewHelper", and use the code below
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;
}
}
}
the above helps us to create recursive <ul><li> HTML code based on Pid .
Step 5: Now we need to add jstree in our project which will help us convert ul/li html into tree view, so from the top menu, click on Tools -> NuGet Package Manager -> Manage NuGet packages for solution.
Then, hit on Install button to download JSTree and include it in your project.
HomeController
to get data from databaseusing System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using TreeviewMVC.Models;
namespace TreeviewMVC.Controllers
{
public class HomeController : Controller
{
TreeviewMVCEntities context = new TreeviewMVCEntities();
public ActionResult Index()
{
return View(context.Categories.Where(x => !x.Pid.HasValue).ToList());
}
}
}
last step go to the Index view and add the required html code to render tree view using jqTree and HTMLhelper which we created earlier
@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.Categories1)
.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>
}
After completing the above steps run your prjoect and output will be as below
Vinnu
Hello, Thanks
As stated in your issue you are passing data of type "System.Collections.Generic.List`1[NSPIRA_IMS.TBL_ACCESSRIGHTS]" in the view but you need to pass data of type "System.Collections.Generic.IEnumerable`1[NSPIRA_IMS.Models.Category]"
Check your Controller C# code, you are passing the wrong model type, here is the similar question link
https://qawithexperts.com/questions/100/the-model-item-passed-into-the-dictionary-is-of-type-systemc
Check all the comments/answers carefully in the above link, similar issue was solved here.
You just need to pass correct List of Model(NSPIRA_IMS.Models.Category) from C# Controller to the view.