Creating a tree like left menu in Sitecore using UserControls and C#

This post is a follow-up on the article on how to create a tree like left menu in Sitecore using XSLT. This time I will not use XSLT to create my left menu, but instead I will use C# and UserControls.

With Sitecore 5 and Sitecore 6, XSLT’s are becomming deprecated, as it is getting easier and easier to create the same using UserControls.

There are several ways of creating a left menu in C#. In this article I will show how you can dump your left menu into an XML string that can be passed to an asp:XmlDataSource. From the XmlDataSource you can hook the menu into the asp:Menu, the asp:TreeView and even the asp:SiteMap controls. However, none of these controls give you full control of the HTML generated (unless you use these control adaptors), so I will show how to use nested repeaters to create the menu hierachy.

First I must create an XmlLeftMenu class that can dump the current left menu structure into an XML string. This code is an pretty accurate dump of how the XSLT did it:

public class XmlLeftMenu
{
  private Item _root;
  private StringBuilder _sb = new StringBuilder();

  public XmlLeftMenu(Item root)
  {
    _root = root;
  }

  public string LeftMenuXml
  {
    get
    {
      _sb.Append("<menu>");
      RenderMenu(_root);
      _sb.Append("</menu>");
      return _sb.ToString();
    }
  }

  private void RenderMenu(Item root)
  {
    ChildList children = root.GetChildren();
    foreach (Item child in children)
    {
      _sb.AppendFormat(@"<item title=""{0}"" url=""{1}"" haschildren=""{2}"" open=""{3}"" selected=""{4}"">",
      child.DisplayName,
      LinkManager.GetItemUrl(child),
      child.HasChildren,
      child.Axes.IsAncestorOf(Sitecore.Context.Item),
      child.ID == Sitecore.Context.Item.ID);
      if (child.HasChildren && child.Axes.IsAncestorOf(Sitecore.Context.Item))
        RenderMenu(child);
      _sb.Append("</item>");
    }
  }
}

(In this example I simply use the item’s DisplayName as my title, but you should modify the code to select the field containing the page title).

The XML contains the page title and page URL. Haschildren is true if the item has children. Open is true if the current item is a child of the menu item being rendered (indicating that the menu is opened and visible). Selected is true if the menu item is the current item. These last 3 attriubutes can be used to style the menu.

The XmlLeftMenu class can be hooked into a XmlDataSource like this: This is the .aspx page code:

<asp:XmlDataSource ID="xmlMenu" EnableCaching="false" runat="server" XPath="menu/item">
</asp:XmlDataSource>

Please note the EnableCaching=false. This is needed as the control caches the XML by default. If caching is enabled, your menu will not change every time you change page. Here is the codebehind:

protected void Page_Load(object sender, EventArgs e)
{
  XmlLeftMenu leftMenu = new XmlLeftMenu(Sitecore.Context.Database.GetItem(Sitecore.Context.Site.StartPath ));
  xmlMenu.Data = leftMenu.LeftMenuXml;
  DataBind();
}

Now your page has access to the left menu. As said before, you can use the asp:Menu to display the menu directly, but if you would like to keep control of your HTML, you can use a series of nested repeaters like this:

<asp:Repeater ID="repLeftmenu" DataSourceID="xmlMenu" runat="server" EnableViewState="false">
  <HeaderTemplate>
    <ul class="leftMenu">
  </HeaderTemplate>
  <ItemTemplate>
    <li>
      <%-- Rendering level 1 --%>
      <a href="<%# XPath("@url") %>">
        <%# XPath("@title") %>
      </a>
      <%-- Nested repeater rendering level 2 --%>
      <asp:Repeater runat="server" ID="level2" EnableViewState="false" DataSource='<%# XPathSelect("item") %>' Visible='<%# Convert.ToBoolean( XPath("@open") ) %>'>
        <HeaderTemplate>
          <ul>
        </HeaderTemplate>
        <ItemTemplate>
          <li>
            <a href="<%# XPath("@url") %>">
              <%# XPath("@title") %>
            </a>
            <%-- Nested repeater rendering level 3 --%>
            <asp:Repeater runat="server" ID="level3" EnableViewState="false" DataSource='<%# XPathSelect("item") %>' Visible='<%# Convert.ToBoolean( XPath("@open") ) %>'>
              <HeaderTemplate>
                <ul>
              </HeaderTemplate>
              <ItemTemplate>
                <li>
                  <a href="<%# XPath("@url") %>">
                    <%# XPath("@title") %>
                  </a>
                  <%-- Nested repeater rendering level 4
                       You can continue for as many leves as you like...
                  --%>
                  <asp:Repeater runat="server" ID="level4" EnableViewState="false" DataSource='<%# XPathSelect("item") %>' Visible='<%# Convert.ToBoolean( XPath("@open") ) %>'>
                    <HeaderTemplate>
                      <ul>
                    </HeaderTemplate>
                    <ItemTemplate>
                      <li>
                        <a href="<%# XPath("@url") %>">
                          <%# XPath("@title") %>
                        </a>
                      </li>
                    </ItemTemplate>
                    <FooterTemplate>
                      </ul>
                    </FooterTemplate>
                  </asp:Repeater>
                </li>
              </ItemTemplate>
              <FooterTemplate>
                </ul>
              </FooterTemplate>
            </asp:Repeater>
          </li>
        </ItemTemplate>
        <FooterTemplate>
          </ul>
        </FooterTemplate>
      </asp:Repeater>
    </li>
  </ItemTemplate>
  <FooterTemplate>
    </ul>
  </FooterTemplate>
</asp:Repeater>

You can extend the presentation by using the “haschildren”, “open” and “selected” attributes from the XML to create style sheet attributes. Extend each LI tag with the following:

<li class="children<%# XPath(@haschildren") %> open<%# XPath("@open") %> selected<%# XPath("@selected") %>">

This will give you class tags like this childrenTrue and childrenFalse, openTrue and openFalse, selectedTrue and selectedFalse.

About briancaos

Developer at Pentia A/S since 2003. Have developed Web Applications using Sitecore Since Sitecore 4.1.
This entry was posted in c#, Sitecore 6 and tagged , , , , , , , , , , . Bookmark the permalink.

5 Responses to Creating a tree like left menu in Sitecore using UserControls and C#

  1. Pingback: Creating a Left Menu » Glass

  2. Max S. says:

    Awesome post. Will definitely use this in my code, if you don’t mind.

    Like

  3. E-Pro says:

    The sub-navigation is getting rendered only for the page that loads. For other pages it’s not rendering the sub-menu. Any solutions for this?

    Like

  4. Maru says:

    Great Article. I was able to render the menu, submenus for each page. But for each page I have a logic to find it parent item which may be first parent ….3 parent item. Then from there I have create the main menu and sub menu based on field values. I am getting Item threshold exceeds in log file. How can I improve this?

    Like

  5. Maru says:

    oh off course I did the caching performance tuning and database tuning and build the indexes also.

    Like

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.