Hierarchical Menu in MVC.NET driven by XML
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?
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>
Have a look at MvcSiteMap provider that works also with XML
精彩评论