Introduction
In this article, we will create dynamics tree view menu using ASP.NET MVC 4, C#, Razor and of course, SQL Server (I use Visual Studio 2015 Community Edition and SQL Server 2012 Express). We will populate menu items (menu names, URIs, and icons) from our SQL table Menu
into a .NET datatable and manipulate it into DOM string, and then render it with Razor function @Html.Raw()
in the View code. The word "dynamics" here indicates that we can create multilevel tree view menu, no matter how many tree levels there are. We will do it in a dirty-simple way; with this method, we can get multi-level tree view menu, not only -hardcoded- two or three tree levels like you see in many tutorials.
Background
In a web application, especially for admin web applications, we have to create a tree view menu, maybe in a sidebar or the header of our web application. Instead of hardcoding the tree view menu in the View, we can create it dynamically, populate the menu item from table Menu in database, and then render it in View. In this article, we will use ASP.NET MVC 4 (using C#) empty template to demonstrate how to create dynamic tree view menus from scratch. You can modify and simplify this source code whenever you want.
Preparation
Before we go far, we create table Menu
in database (in your existing database, or you can create a new one), the table structure is shown below, column Id
is the primary key, and set it auto-increment.
This is the script to create the table:
CREATE TABLE [dbo].[Menu] (
[Id] [int] IDENTITY(1,1) NOT NULL,
[MenuNumber] [int] NOT NULL,
[ParentNumber] [int] NULL,
[MenuName] [varchar](50) NOT NULL,
[Uri] [varchar](50) NULL,
[Icon] [varchar](50) NOT NULL,
CONSTRAINT [PK_Menu] 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]
And then, insert the table with some values, the table will look like this:
This is the script to insert table Menu
to get the result like the picture above:
INSERT INTO [dbo].[Menu]([MenuNumber], [ParentNumber], [MenuName], [Uri], [Icon])
VALUES(0, NULL, 'MAIN NAVIGATION', '', 'glyphicon glyphicon-dashboard')
INSERT INTO [dbo].[Menu]([MenuNumber], [ParentNumber], [MenuName], [Uri], [Icon])
VALUES(1000, 0, 'Dashboard', 'dashboard', 'glyphicon glyphicon-dashboard')
INSERT INTO [dbo].[Menu]([MenuNumber], [ParentNumber], [MenuName], [Uri], [Icon])
VALUES(2000, 0, 'User', '', 'glyphicon glyphicon-user')
INSERT INTO [dbo].[Menu]([MenuNumber], [ParentNumber], [MenuName], [Uri], [Icon])
VALUES(3000, 0, 'Setting', '', 'glyphicon glyphicon-cog')
INSERT INTO [dbo].[Menu]([MenuNumber], [ParentNumber], [MenuName], [Uri], [Icon])
VALUES(2010, 2000, 'Profile', 'user/profile', 'glyphicon glyphicon-sunglasses')
INSERT INTO [dbo].[Menu]([MenuNumber], [ParentNumber], [MenuName], [Uri], [Icon])
VALUES(2020, 2000, 'My File', 'user/myfile', 'glyphicon glyphicon-folder-open')
INSERT INTO [dbo].[Menu]([MenuNumber], [ParentNumber], [MenuName], [Uri], [Icon])
VALUES(2021, 2020, 'Document', 'user/myfile/document', 'glyphicon glyphicon-folder-close')
INSERT INTO [dbo].[Menu]([MenuNumber], [ParentNumber], [MenuName], [Uri], [Icon])
VALUES(2022, 2020, 'Music', 'user/myfile/music', 'glyphicon glyphicon-music')
INSERT INTO [dbo].[Menu]([MenuNumber], [ParentNumber], [MenuName], [Uri], [Icon])
VALUES(3010, 3000, 'General Setting', 'setting/general', 'glyphicon glyphicon-wrench')
INSERT INTO [dbo].[Menu]([MenuNumber], [ParentNumber], [MenuName], [Uri], [Icon])
VALUES(3020, 3000, 'Privacy', 'setting/privacy', 'glyphicon glyphicon-lock')
Note that, when a menu doesn't have any parent number (NULL
), then that menu is the (outer) parent of all menus. Here, for the child menus, we use menu number 1000
, for example. It has parent number 0
and it has child numbers 1010
, 1020
, 1030
, and so on; and when a menu with number 1020
has child menu, its child menu number will be 1021
, 1022
, and so on; and when menu number 1021
has children, the child number will be 10211
, 10212
, and so on; and so on. This is just a convention, you can use your own menu numbering.
Using the Code
We already have the table Menu
in our database, we also have the data. Now, we can write the source code. First, open Visual Studio and then create a new project with template Visual C# - ASP.NET Web Application, name the project TreeViewMenu
. Select the Empty template with add folder and core references for MVC. Visual Studio will create the empty template MVC for us.
Create a controller in folder Controllers, name it HomeController
. Then, right click in method ActionResult
Index()
and Add a View for it, name the view Index
. Overwrite the view Index.cshtml (in folder ~/Views/Home) with this code:
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Index</title>
<link href="~/Contents/css/bootstrap.css" rel="stylesheet" type="text/css" />
</head>
<body>
<div>
@Html.Raw(ViewBag.DOM_TreeViewMenu)
</div>
<script src="~/Contents/js/jquery-1.11.3.min.js" type="text/javascript"></script>
<script src="~/Contents/js/bootstrap.js" type="text/javascript"></script>
</body>
</html>
Save the file. In the center of cshtml code above, you can see the razor function (or helper) @Html.Raw()
, this function will render a raw string of HTML code (DOM) when you open it in your web browser. Next, we will set the value of object ViewBag.DOM_TreeViewMenu
with raw HTML code of tree view menu in our HomeController
.
Now, we can go to folder Contents, add folder css, fonts, and js in that folder. We use standard bootstrap, glyphicons font and jQuery (you can download it for free). Here, we only need to get the glyphicon to demonstrate tree view menu with icon. In Solution Explorer, you can see the structure of folder Contents is like this picture.
In HomeController
, we create three private
methods. see this skeleton code.
using System.Data;
using System.Data.SqlClient;
using System.Linq;
using System.Web.Mvc;
namespace TreeViewMenu.Controllers
{
public class HomeController : Controller
{
private SqlConnection conn;
private SqlDataAdapter da;
private DataTable dt;
public ActionResult Index()
{
ViewBag.DOM_TreeViewMenu = PopulateMenuDataTable();
return View();
}
private string PopulateMenuDataTable()
{
string DOM = "";
return DOM;
}
private string GetDOMTreeView(DataTable dt)
{
string DOMTreeView = "";
return DOMTreeView;
}
private string CreateTreeViewOuterParent(DataTable dt)
{
string DOMDataList = "";
return DOMDataList;
}
private string CreateTreeViewMenu(DataTable dt, string ParentNumber)
{
string DOMDataList = "";
return DOMDataList;
}
}
}
We start from method PopulateMenuDataTable()
. Here, we will populate data from our SQL table Menu
into DataTable
. This is the code.
private string PopulateMenuDataTable()
{
string DOM = "";
string sql = @"SELECT MenuNumber, ParentNumber, MenuName, Uri, Icon FROM Menu";
conn = new SqlConnection(@"Data Source = YOUR_SERVERNAME;
Initial Catalog = YOUR_DATABASE;
User ID = sa; Password = YOUR_PASSWORD");
conn.Open();
da = new SqlDataAdapter(sql, conn);
da.SelectCommand.CommandTimeout = 10000;
dt = new DataTable();
da.Fill(dt);
if (conn.State == ConnectionState.Open)
{
conn.Close();
}
conn.Dispose();
DOM = GetDOMTreeView(dt);
return DOM;
}
Then to get the DOM string of the tree view, we write the body of method GetDOMTreeView()
and two other methods.
private string GetDOMTreeView(DataTable dt)
{
string DOMTreeView = "";
DOMTreeView += CreateTreeViewOuterParent(dt);
DOMTreeView += CreateTreeViewMenu(dt, "0");
DOMTreeView += "</ul>";
return DOMTreeView;
}
We populate the DOM string for header (Outer Parent Menu) from method CreateTreeViewOuterParent()
.
private string CreateTreeViewOuterParent(DataTable dt)
{
string DOMDataList = "";
DataRow[] drs = dt.Select("MenuNumber = 0");
foreach (DataRow row in drs)
{
DOMDataList = "<ul><li class='header'>" + row[2].ToString() + "</li>";
}
return DOMDataList;
}
And then, we populate all DOM strings from table Menu, we do it recursively in method CreateTreeViewMenu()
, you can look at the method body that the method calls itself in order to get all tree levels from table Menu. As you can also see, we use Lambda expression to filter result of datatable and then store the value into DataRow[]
, so we can loop the DataRow
s and get the value easily.
private string CreateTreeViewMenu(DataTable dt, string ParentNumber)
{
string DOMDataList = "";
string menuNumber = "";
string menuName = "";
string uri = "";
string icon = "";
DataRow[] drs = dt.Select("ParentNumber = " + ParentNumber);
foreach (DataRow row in drs)
{
menuNumber = row[0].ToString();
menuName = row[2].ToString();
uri = row[3].ToString();
icon = row[4].ToString();
DOMDataList += "<li class='treeview'>";
DOMDataList += "<a href='" + uri + "'><i class='" + icon + "'></i><span> "
+ menuName + "</span></a>";
DataRow[] drschild = dt.Select("ParentNumber = " + menuNumber);
if (drschild.Count() != 0)
{
DOMDataList += "<ul class='treeview-menu'>";
DOMDataList += CreateTreeViewMenu(dt, menuNumber).Replace
("<li class='treeview'>", "<li>");
DOMDataList += "</ul></li>";
}
else
{
DOMDataList += "</li>";
}
}
return DOMDataList;
}
That's all! Debug the project (I use Google Chrome) and we will get the result like this picture. As you can see, Menu Name and its Icon are shown; and if you point your pointer to a Menu (let's say My File), you can see that the Uri is also successfully loaded to View.
If we want to add a child menu, let's say for menu Music
, name it Dangdut
. We only need to insert it into our table Menu
, and no need to change our source code. To demonstrate it, use this script to insert menu Dangdut
.
INSERT INTO [dbo].[Menu]([MenuNumber], [ParentNumber], [MenuName], [Uri], [Icon])
VALUES(20221, 2022, 'Dangdut', 'https://en.wikipedia.org/wiki/Dangdut', _
'glyphicon glyphicon-globe')
Now, the result will look like this:
Points of Interest
This is a dirty way of creating Tree View Menu in ASP.NET MVC, instead of using DOM string like this, you can reduce source code's line with a clean code by manipulating the DOM string using useful List
. In real web application, tree view menu is in a master layout View, so you can create a layout view in Shared folder that contains a View with the tree view menu code. With a little enhancement with bootstrap and jQuery or some layout template in our code, we can create a classy navigation tree view menu in our web application.
History
- 10th February, 2016: Initial version