views:

833

answers:

2

Trying to build a recursive html menu using c#. The html structure I need is as follows:

<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 could obviously change as it is dynamic. At the moment, I'm using HtmlGeneric controls, i.e. ul, li and adding the controls, but not sure how to turn this into a efficient recursive function.

+3  A: 

I'm not sure what you're structure for keeping the string hierarchy is but let's assume you have some way of getting the child strings of each string as required (e.g. you can get 'Beds' and 'Desks' from 'Home').

First I would declare the 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);
}

And separate out the tab adding into 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 simply call GenerateHTML with a new string builder, the first level of strings and the tab index set at 0 and it should give you what you want. I haven't included the functionality to get the child strings as I wasn't sure what kind of structure you are using to do that - let me know and I can adapt my solution.

Hope that helps, Dane.

link664
A: 

The only thing recursive about a list like that is the srtructure of the data. NGenerics is a good open source library of of data structures.

Also I would reccomend using the HTMLTextWriter Class in this problem.

If you want take the roll-your-own approach and create a server controll then something like the class below would 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();
   }


}
Gus