Creating a sub menu from datatable using C # as the ul list, not Asp.net menu control

I need to create a custom menu that has a submenu using the ul list from a DataTable

Below is a sample database and a sample HTML ul list with dummy data.

 PID MENU Handler PageLangID ParentID IssueID CatID MenuPosition ----------- -------------------- ------------------------ ----------- ----------- ----------- ----------- ------------ 6 Business Category.aspx 1 6 1 16 1 6 Culture Category.aspx 1 6 1 3 1 6 Economy Category.aspx 1 6 1 2 1 6 Finance Category.aspx 1 6 1 19 1 6 Infrastructure Category.aspx 1 6 1 17 1 6 Lifestyle Category.aspx 1 6 1 20 1 6 Others Category.aspx 1 6 1 21 1 6 People Category.aspx 1 6 1 7 1 6 Politics Category.aspx 1 6 1 1 1 6 Sports Category.aspx 1 6 1 4 1 12 1002 Default.aspx 1 12 3 1 1 12 1003 Default.aspx 1 12 4 1 1 12 1006 Default.aspx 1 12 1 1 1 12 1009 Default.aspx 1 12 5 1 1 1 Home Default.aspx 1 0 1 1 10 11 Video Videos.aspx 1 10 1 1 10 2 About Us Page.aspx 1 0 1 1 20 5 Articles Articles.aspx 1 0 1 1 20 6 Categories Category.aspx 1 0 

DESIRED HTML OUTPUT

 <div id="nav-wrapper"> <ul id="nav" class="dropdown dropdown-linear" > <li><span class="dir"><a href="./">Home</a></span></li> <li ><span class="dir"><a href="ultimate.linear.html">About Us</a></span> <ul > <li><a href="./">History</a></li> <li><a href="./">Our Vision</a></li> <li><a href="./">The Team</a></li> <li><a href="./">Clients</a></li> <li><a href="./">Testimonials</a></li> <li><a href="./">Press</a></li> <li><a href="./">FAQs</a></li> </ul> </li> <li class="active" ><span class="dir"><a href="ultimate.linear-active.html">Categories</a></span> <ul> <li><a href="./">Politics</a></li> <li><a href="./">Economy</a></li> <li><a href="./">Finance</a></li> <li><a href="./">Business</a></li> <li><a href="./">Group News</a></li> <li><a href="./">Culture</a></li> <li><a href="./">Lifestyle</a></li> <li><a href="./">Sports</a></li> <li><a href="./">Infrastructure</a></li> <li><a href="./">Book Review</a></li> <li><a href="./">Others</a></li> </ul> </li> </ul> </div> 

I do not want to use nested repeater elements. I would appreciate sample code that I can work with.

UPDATED : this code does not work, definitely I am doing something wrong

 protected void Page_Load(object sender, EventArgs e) { DataSet ds = new DataSet(); ds = DataProvider.Connect_Select(strSql); DataTable dt = ds.Tables[0]; //dt.Select("ParentID == 0") ; var s = GenerateUL(dt.Select("ParentID == 0")); Response.Write(s); } private string GenerateUL(var menus) { var sb = new StringBuilder(); sb.AppendLine("<ul>"); foreach (var menu in menus) { if (menu.Menus.Any()) { sb.AppendLine("<li>" + menu.Text); sb.Append(GenerateUL(menu.Menus.AsQueryable())); sb.AppendLine("</li>"); } else sb.AppendLine("<li>" + menu.Text + "</li>"); } sb.AppendLine("</ul>"); return sb.ToString(); } 

LATEST UPDATE BASED ON DANI SOLUTION

It seems to work fine with its sample data, but when I use it with my actual data, it throws a StackOverflowException that was unhandled.

Below is a screenshot of the error. enter image description here

The error occurs when it goes into the infinate loop type, where PID=6 My actual data, which are shown above, have 23 records and are the result of UNION from different tables, which is the reason that I have several rows in the table, where PID=6 I am afraid that he will also do the same as pid=12 .

Even if I catch the exception, my site still crashes because of this ...

Latest CODE

 protected void Page_Load(object sender, EventArgs e) { string strSql = "SELECT DISTINCT PID, MENU, Handler,PageLangID, ParentID,IssueID, CatID,MenuPosition FROM MENUTABLE "; DataSet ds = new DataSet(); ds = DataProvider.Connect_Select(strSql); DataTable table = ds.Tables[0]; DataRow[] parentMenus = table.Select("ParentId = 0"); var sb = new StringBuilder(); string unorderedList = GenerateUL(parentMenus, table, sb); } private string GenerateUL(DataRow[] menu, DataTable table, StringBuilder sb) { sb.AppendLine("<ul>"); try { if (menu.Length > 0) { foreach (DataRow dr in menu) { ctr = ctr + 1; string handler = dr["Handler"].ToString(); string menuText = dr["MENU"].ToString(); string line = String.Format(@"<li><a href=""{0}"">{1}</a>", handler, menuText); sb.Append(line); string pid = dr["PID"].ToString(); DataRow[] subMenu = table.Select(String.Format("ParentId = {0}", pid)); if (subMenu.Length > 0) { var subMenuBuilder = new StringBuilder(); sb.Append(GenerateUL(subMenu, table, subMenuBuilder)); } sb.Append("</li>"); } } } catch (Exception ex) { } sb.Append("</ul>"); return sb.ToString(); } 

Update: When I change my query, which gives me the result below, it works fine, but it would be nice to make it work with the actual data that first appears in the question.

 PID MENU Handler PageLangID ParentID IssueID CatID MenuPosition ----------- -------------------- ------------------------ ----------- ----------- ----------- ----------- ------------ 1 Home Default.aspx 1 0 1 1 10 2 About Us Page.aspx 1 0 1 1 20 3 News News.aspx 1 0 1 1 30 5 Articles Articles.aspx 1 0 1 1 20 6 Categories Category.aspx 1 0 1 1 25 
+4
source share
1 answer

I created a slightly simplified table structure on my side, as for example we only need the following columns:

  • PID
  • MENU
  • Handler
  • Parentid

Sample data:

As you can see from this example, we have a 3-way deep hierarchy for Product, and the rest of the elements have no children.

enter image description here

Code behind:

The code below does the following:

  • First it receives all the elements without parents and starts to scroll through them.
  • Checks if an element is the parent of any node and resolves its children recursively.

     protected void Page_Load(object sender, EventArgs e) { DataSet ds = new DataSet(); ds = DataProvider.Connect_Select("SELECT * FROM Menu"); DataTable table = ds.Tables[0]; DataRow[] parentMenus = table.Select("ParentId = 0"); var sb = new StringBuilder(); string unorderedList = GenerateUL(parentMenus, table,sb); Response.Write(unorderedList); } private string GenerateUL(DataRow[] menu,DataTable table,StringBuilder sb) { sb.AppendLine("<ul>"); if (menu.Length > 0) { foreach (DataRow dr in menu) { string handler = dr["Handler"].ToString(); string menuText = dr["MENU"].ToString(); string line = String.Format(@"<li><a href=""{0}"">{1}</a>",handler,menuText); sb.Append(line); string pid = dr["PID"].ToString(); DataRow[]subMenu = table.Select(String.Format("ParentId = {0}", pid)); if (subMenu.Length > 0) { var subMenuBuilder = new StringBuilder(); sb.Append(GenerateUL(subMenu, table, subMenuBuilder)); } sb.Append("</li>"); } } sb.Append("</ul>"); return sb.ToString(); } 

Final result:

enter image description here

EDIT:

The problem is that the code creates a menu based on ParentID, and for ID = 6, ParentID also has a value of 6. Thus, 6 calls 6, and we have an infinite loop, excellent.

Now that we know what the problem is, we can put a simple check to create a submenu recursively only if ParentId != PID , here is the updated code:

 private string GenerateUL(DataRow[] menu, DataTable table, StringBuilder sb) { sb.AppendLine("<ul>"); if (menu.Length > 0) { foreach (DataRow dr in menu) { string handler = dr["Handler"].ToString(); string menuText = dr["MENU"].ToString(); string line = String.Format(@"<li><a href=""{0}"">{1}</a>", handler, menuText); sb.Append(line); string pid = dr["PID"].ToString(); string parentId = dr["ParentId"].ToString(); DataRow[] subMenu = table.Select(String.Format("ParentId = {0}", pid)); if (subMenu.Length > 0 && !pid.Equals(parentId)) { var subMenuBuilder = new StringBuilder(); sb.Append(GenerateUL(subMenu, table, subMenuBuilder)); } sb.Append("</li>"); } } sb.Append("</ul>"); return sb.ToString(); } 

I think it’s important here to understand how this code works by stepping it in the debugger, so you can change if any other problems arise.

+9
source

All Articles