0

I am trying to create a hierarchical xml based menu in MVC.NET

<?xml version="1.0" encoding="utf-8" ?>
<NavigationMenu id="1" Name="myMenu" Lang="EN">
  <NavigationMenuGroup Header="Home" Name="header1" Link="/home" />
  <NavigationMenuGroup Header="Manage" Name="header2" Link="/options" />
  <NavigationMenuGroup Header="About" Name="header3"  Link="/About" >
    <NavigationMenuItem Header="Test1" Name="header5"  Link="/page1" />
    <NavigationMenuItem Header="Test2" Name="header6"  Link="/page2" />
  </NavigationMenuGroup>
</NavigationMenu>

I pan for the masterpage to Render a Partial menu (NavMenu.ascx), the data will be fed to the masterpage by a Custom BaseController.

The NavMenu.ascx will produce code similar to following (easy to plugin jQuery or simlar).

 <ul id="menu">
   <li>
      <h2><a href="/home">Home</a></h2>
   </li>

   <li>
      <h2><a href="/options">Manage</a></h2>
   </li>

   <li>
      <h2><a href="/about">About</a></h2>

          <li>
             <h2><a href="/page2">Test1</a></h2>
          </li>
          <li>
             <h2><a href="/page1">Test2</a></h2>
          </li>
   </li>
</ul>

I also have setup a set of classes to Deserialize.

    namespace MyMVC.Helpers
    {
        public class XmlSerializerHelper<T>
        {
            public Type _type;

            public XmlSerializerHelper()
            {
                _type = typeof(T);
            }

            public void Save(string path, object obj)
            {
                using (TextWriter textWriter = new StreamWriter(path))
                {
                    XmlSerializer serializer = new XmlSerializer(_type);
                    serializer.Serialize(textWriter, obj);
                }
            }

            public T Read(string path)
            {
                T result;
                using (TextReader textReader = new StreamReader(path))
                {
                    XmlSerializer deserializer = new XmlSerializer(_type);
                    result = (T)deserializer.Deserialize(textReader);
                }
                return result;
            }
        }
}

I am not sure what is the best way to do this without breaking the MVC principals?

1
  • Is every item in your menu actually a header? Commented Jun 19, 2010 at 16:07

2 Answers 2

3

Was trying to solve this problem myself and came up with the following. I used mine for a primary navigation which needed certain active classes (based on Drupal's menu rendering):

XML:

<?xml version="1.0" encoding="utf-8" ?>
<items>
  <item title="Some Title" name="Test" text="Testing 1" link="1">
    <items>
      <item name="Test" text="Testing 1-1" link="2">
        <items>
          <item name="Test" text="About" link="/home/about" />
          <item name="Test" text="Testing 1-1-2" link="4" />
          <item name="Test" text="Testing 1-1-3" link="5" />
        </items>
      </item>
      <item name="Test" text="Testing 1-2" link="6" />
      <item name="Test" text="Testing 1-3" link="7" />
    </items>
  </item>
  <item name="Testame" text="Testing 2" link="8" />
  <item name="Test" text="Testing 3" link="9" />
  <item name="Test" text="Testing 4" link="10" />
  <item name="Test" text="Testing 5" link="11" />
  <item title="Some Title" name="Test" text="Testing 6" link="1">
    <items>
      <item name="Test" text="Testing 6-1" link="12">
        <items>
          <item name="Test" text="Testing 6-1-1" link="13" />
          <item name="Test" text="Testing 6-1-2" link="14" />
          <item name="Test" text="Testing 6-1-3" link="15" />
        </items>
      </item>
      <item name="Test" text="Testing 6-2" link="16" />
      <item name="Test" text="Testing 6-3" link="17" />
    </items>
  </item>
  <item name="Test" text="Testing 7" link="18" />
  <item name="Test" text="Testing 8" link="19">
    <items>
      <item name="Test" text="Testing 8-1" link="20" />
      <item name="Test" text="Testing 8-2" link="21" />
      <item name="Test" text="Testing 8-3" link="22" />
    </items>
  </item>
</items>

Helper:

    public static class Helpers
    {

        public static MvcHtmlString Menu(this HtmlHelper helper, string name, string e = null, object attributes = null)
        {
            string filepath = helper.ViewContext.HttpContext.Server.MapPath(name + ".xml");

            XPathDocument oDoc = new XPathDocument(filepath);

            return new MvcHtmlString(GetMenuHtml(oDoc.CreateNavigator(), e, attributes));
        }

        private static string GetMenuHtml(XPathNavigator nav, string e = "ul", object attributes = null)
        {

            List<String> item = new List<String>();

            XPathNodeIterator nodes = nav.Select("items/item");

            TagBuilder holder = new TagBuilder(e);

            if (attributes != null)
            {
                IDictionary<string, object> htmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(attributes);
                holder.MergeAttributes(htmlAttributes);
            }

            while (nodes.MoveNext())
            {

                TagBuilder a = new TagBuilder("a");
                TagBuilder span = new TagBuilder("span");
                TagBuilder li = new TagBuilder("li");

                string url = nodes.Current.GetAttribute("link", string.Empty);
                string text = nodes.Current.GetAttribute("text", string.Empty);
                string title = nodes.Current.GetAttribute("title", string.Empty);

                span.InnerHtml = text;

                a.MergeAttribute("href", url);

                if (!String.IsNullOrEmpty(title)) a.MergeAttribute("title", title);

                a.InnerHtml = span.ToString();

                List<String> classes = new List<String>();

                if (url == HttpContext.Current.Request.Url.AbsolutePath)
                {
                    classes.Add("active");   
                }

                if (nodes.Count == nodes.CurrentPosition)
                {
                    classes.Add("last");
                }
                else if (nodes.CurrentPosition == 1)
                {
                    classes.Add("first");
                }

                if (nodes.Current.HasChildren)
                {
                    li.InnerHtml = a.ToString() + GetMenuHtml(nodes.Current, e);

                    if (li.InnerHtml.Contains("class=\"active"))
                    {
                        classes.Add("active-trail");
                    }

                    classes.Add("leaf");
                }
                else
                {
                    li.InnerHtml = a.ToString();
                }

                if(classes.Count > 0){
                    li.MergeAttribute("class", string.Join(" ", classes.ToArray()));
                }

                item.Add(li.ToString() + "\n");

            }

            holder.InnerHtml = string.Join("", item.ToArray());

            return holder.ToString();

        }


    }

View:

@Html.Menu("navigation", "ul")

Output

<ul>
    <li class="first active-trail leaf">
        <a href="1" title="Some Title"><span>Testing 1</span></a>
        <ul>
            <li class="first active-trail leaf"><a href="2"><span>Testing 1-1</span></a>
                <ul>
                    <li class="active first"><a href="/home/about"><span>Testing 1-1-1</span></a></li>
                    <li><a href="4"><span>Testing 1-1-2</span></a></li>
                    <li class="last"><a href="5"><span>Testing 1-1-3</span></a></li>
                </ul>
            </li>
            <li><a href="6"><span>Testing 1-2</span></a></li>
            <li class="last"><a href="7"><span>Testing 1-3</span></a></li>
        </ul>
    </li>
    <li><a href="8"><span>Testing 2</span></a></li>
    <li><a href="9"><span>Testing 3</span></a></li>
    <li><a href="10"><span>Testing 4</span></a></li>
    <li><a href="11"><span>Testing 5</span></a></li>
    <li class="leaf">
        <a href="1" title="Some Title"><span>Testing 6</span></a>
        <ul>
            <li class="first leaf"><a href="12"><span>Testing 6-1</span></a>
                <ul>
                    <li class="first"><a href="13"><span>Testing 6-1-1</span></a></li>
                    <li><a href="14"><span>Testing 6-1-2</span></a></li>
                    <li class="last"><a href="15"><span>Testing 6-1-3</span></a></li>
                </ul>
            </li>
            <li><a href="16"><span>Testing 6-2</span></a></li>
            <li class="last"><a href="17"><span>Testing 6-3</span></a></li>
        </ul>
    </li>
    <li><a href="18"><span>Testing 7</span></a></li>
    <li class="last leaf"><a href="19"><span>Testing 8</span></a>
        <ul>
            <li class="first"><a href="20"><span>Testing 8-1</span></a></li>
            <li><a href="21"><span>Testing 8-2</span></a></li>
            <li class="last"><a href="22"><span>Testing 8-3</span></a></li>
        </ul>
    </li>
</ul>
Sign up to request clarification or add additional context in comments.

Comments

2

Have a look at MvcSiteMap provider that works also with XML

1 Comment

this is useful, but Im looking for a neat way to hand roll this :)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.