Recursive menu builder with C #

Trying to create a recursive html menu using C #. I need html structure:

<ul>
   <li>Office</li>
   <li>Home
      <ul>
         <li>Beds</li>
         <li>Desks</li>
      </ul>
   </li>
   <li>Outdoor
      <ul>
         <li>Children
            <ul>
               <li>Playsets</li>
            </ul>
         </li>
      </ul>
   </li>
</ul>

      

The structure can obviously change as it is dynamic. At the moment I am using the HtmlGeneric controls i.e. Ul, li and adding controls, but not sure how to turn this into an efficient recursive function.

+2


source to share


3 answers


I'm not sure if you are a structure for storing a hierarchy of rows, but let's assume you have a way to get the child rows of each row as needed (for example, you can get "Beds" and "Tables" from "Home").

First, I declare tags as constants:

 public const string OPEN_LIST_TAG = "<ul>";
 public const string CLOSE_LIST_TAG = "</ul>";
 public const string OPEN_LIST_ITEM_TAG = "<li>";
 public const string CLOSE_LIST_ITEM_TAG = "</li>";

      

Then I would create a recursive method using something like a string builder:

/// <summary>
/// Adds another level of HTML list and list items to a string
/// </summary>
/// <param name="str">The string to add</param>
/// <param name="liStrings">The list of strings at this level to add</param>
/// <param name="iTabIndex">The current number of tabs indented from the left</param>
public void GenerateHTML( System.Text.StringBuilder str, List<string> liStrings, int iTabIndex) {
   //add tabs to start of string
   this.AddTabs(str, iTabIndex);

   //append opening list tag
   str.AppendLine(OPEN_LIST_TAG);

   foreach (string strParent in liStrings) {
      //add tabs for list item
      this.AddTabs(str, iTabIndex + 1);

      //if there are child strings for this string then loop through them recursively
      if (this.GetChildStrings(strParent).Count > 0) {
         str.AppendLine(OPEN_LIST_ITEM_TAG + strParent);
         GenerateHTML(str, this.GetChildStrings(strParent), iTabIndex + 2);

         //add tabs for closing list item tag
         this.AddTabs(str, iTabIndex + 1);
         str.AppendLine(CLOSE_LIST_ITEM_TAG);
      }
      else {
         //append opening and closing list item tags
         str.AppendLine(OPEN_LIST_ITEM_TAG + strParent + CLOSE_LIST_ITEM_TAG);
      }
   }

   //add tabs for closing list tag
   this.AddTabs(str, iTabIndex);
   //append closing list tag
   str.AppendLine(CLOSE_LIST_TAG);
}

      



Separate the tab by adding in a separate method:

/// <summary>
/// Appends a number of tabs to the string builder
/// </summary>
/// <param name="str">The string builder to append to</param>
/// <param name="iTabIndex">The number of tabs to append to</param>
public void AddTabs(System.Text.StringBuilder str, int iTabIndex) {
   for (int i = 0; i <= iTabIndex; i++) {
      str.Append("\t");
   }
}

      

Then just call GenerateHTML with a new row builder, first row level and tab index set to 0, and it should give you what you want. I have not included the functions to get the child rows as I was not sure what structure you were using to do this - let me know and I can adapt my solution.

Hope this helps, Dane.

+3


source


A bit outdated topic, but should have the correct answer nonetheless

In my example, the inbound nodes contain a Children property that contains children.



private HtmlGenericControl RenderMenu(Nodes nodes)
{
    if (nodes == null)
        return null;

    var ul = new HtmlGenericControl("ul");

    foreach (Node node in nodes)
    {
        var li = new HtmlGenericControl("li");
        li.InnerText = node.Name;

        if(node.Children != null)
        {
            li.Controls.Add(RenderMenu(node.Children));
        }

        ul.Controls.Add(li);
    }

    return ul;
}

      

+1


source


The only thing that recursively refers to a list like this is the srtructure of the data. NGenerics is a good open source library of data structures.

Also I would recommend using HTMLTextWriter Class in this problem.

If you want to take a "roll-your-own" approach and create a server control then something like the class below will work.

public class MenuTree : Control
{
   public string MenuText {get; set;}
   public List<MenuTree> Children {get; set;}

   public override void Render(HTMLTextWriter writer)
   {
      writer.RenderBeginTag(HtmlTextWriterTag.Ul);
      writer.RenderBeginTag(HtmlTextWriterTag.Li);
      writer.RenderBeginTag(MenuText);
      writer.RenderEndTag();
      foreach (MenuTree m in Children)
      {
         m.Render();
      }
      writer.RenderEndTag();
   }


}

      

0


source







All Articles