使用c#递归菜单构建器

时间:2009-09-08 22:59:31

标签: c# recursion

尝试使用c#构建递归html菜单。我需要的html结构如下:

<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>

结构可能会因动态而明显改变。目前,我正在使用HtmlGeneric控件,即ul,li和添加控件,但不知道如何将其转换为有效的递归函数。

3 个答案:

答案 0 :(得分:3)

我不确定你保留字符串层次结构的结构是什么,但我们假设你有一些方法可以根据需要获取每个字符串的子字符串(例如,你可以从''获得'床'和'桌子'家')。

首先,我将标签声明为常量:

 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>";

然后我会使用类似字符串构建器的东西创建一个递归方法:

/// <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);
}

将选项卡分开添加到单独的方法中:

/// <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");
   }
}

然后简单地使用新的字符串构建器调用GenerateHTML,第一级字符串和选项卡索引设置为0,它应该为您提供所需的内容。我没有包含获取子字符串的功能,因为我不确定您使用什么样的结构 - 让我知道,我可以调整我的解决方案。

希望有所帮助, 戴恩。

答案 1 :(得分:1)

有点老话题,但它应该有正确答案

在我的示例中,传入的节点包含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;
}

答案 2 :(得分:0)

关于这样的列表的唯一递归是数据的结构。 NGenerics是一个很好的数据结构开源库。

此外,我建议在此问题中使用HTMLTextWriter Class

如果你想采用自己动手的方法并创建一个服务器控件,那么下面的类就可以了。

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();
   }


}