views:

2243

answers:

4

I've rewritten this post to make it more simple. This is the code I've got (a HtmlHelper):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Runtime.CompilerServices;
using System.Web.Mvc;
using System.Text;

using System.Web.Routing;

namespace Intranet.Helpers
{
  public static class MenuHelper
  {
    private static string GetBackLink(SiteMapNode parentNode)
    {
      return "<li class='li-back'><a href='" + parentNode.Url + "' title='" + parentNode.Title + "'></a></li>";
    }

    public static string Menu(this HtmlHelper helper)
    {
      var sb = new StringBuilder();
      SiteMapNodeCollection siteMapNodeCollection;
      sb.Append("<ul>");
      SiteMapNode currentNode = SiteMap.CurrentNode;

      if (!SiteMap.CurrentNode.Equals(SiteMap.RootNode))
      {
        if (!SiteMap.CurrentNode.HasChildNodes)
          sb.Append(GetBackLink(SiteMap.CurrentNode.ParentNode.ParentNode));
        else
          sb.Append(GetBackLink(SiteMap.CurrentNode.ParentNode));
      }

      if (!SiteMap.CurrentNode.HasChildNodes)
        siteMapNodeCollection = SiteMap.CurrentNode.ParentNode.ChildNodes;
      else
        siteMapNodeCollection = SiteMap.CurrentNode.ChildNodes;

      foreach (SiteMapNode node in siteMapNodeCollection)
      {
        if(node.Description.Equals("hidden")) continue;

        if (node.Url.Length == 0 && node.Description.Equals("separator"))
          sb.Append("<li class=\"li-separator\"></li>");
        else if (node.Url.Length == 0 && node.Description.Equals("heading"))
          sb.Append("<li class=\"li-heading\">" + node.Title + "</li>");
        else
        {
          if (node.HasChildNodes)
          {
            if (node.NextSibling != null)
              sb.Append("<li class=\"li-sub\"><a href=\"" + node.Url + "\">" + node.Title + "</a></li>");
            else
              sb.Append("<li class=\"li-sub last-child\"><a href=\"" + node.Url + "\">" + node.Title + "</a></li>");
          }
          else
          {
            if (node.NextSibling != null)
              sb.Append("<li><a href='" + node.Url + "'>" + node.Title + "</a></li>");
            else
              sb.Append("<li class='last-child'><a href='" + node.Url + "'>" + node.Title + "</a></li>");
          }
        }
      }

      sb.Append("</ul>");
      return sb.ToString();
    }
  }
}

which is an altered version of this. I'm using MVC Areas Lib so I can't see how MvcSiteMap can work with this as it no longer works by {controller}/{action} like it did before.

Say I have a page like http://localhost/mycontroller/myaction and it exists in the SiteMap, then the menu will be generated fine. But say I do http://localhost/mycontroller/myaction/50 and specify a parameter, the SiteMap generator will no longer work because this URL does not exist. The tutorial doesn't cover MVC Areas Lib, so the solution to this problem doesn't work.

A: 

look, in asp.net the SiteMap is greatly implemented unlike it's 'non standard' implementation in asp.net mvc :). I have to choose also for my app how to organise a site map. I think this solution is far to be ideal and elegant, but it seems very nice for me. In my Base data view object that I use for displaying data on the view I make all this functionality fluently for the sake of commodity (I love fluent code ), so there is there a method that generates hard coded path in dependanse of the ''link''. So what is link?, the link represents a static class with all my links in my site. So for example your :

http://localhost/myarea/contract/viewcontract/12

In ''links'' means: Links.ContractView and in my controller I have smth like this:

BaseDV.SetPageMapBasedOnLink(Links.ContractView)

and in SetPageMapBasedOnLink you have a switch that based on Links value sets the appropriate title or what ever you want. I hope you got the idea, and this will help you to decide!

diadiora
If you check my edit you'll see it auto-generates my menu based on the HtmlHelper as recommended by the article I linked to. I'm not sure, based on your translation, how this would work for me personally. I can see the idea behind it, however.
Kezzer
+4  A: 

Checkout MvcSitemap on Codeplex

Matthew
Yeah, but I'm using areas lib, so I don't see how that would work.
Kezzer
Tested this today, it definitely won't work because MvcSiteMap works on the basis of {controller}/{action} as opposed to area/{controller}/{action}.
Kezzer
Gah, I'm using this now as it has areas support, but I can't mark your answer as right :(
Kezzer
A: 

This code looks a bit off-track to me. The main idea of .NET MVC is separating concerns, so when you populate a StringBuilder object, you're missing the point a bit. What I'd expect is code to return a collection of all possible nodes, and then you send it to your View and it does the actual HTML layouting.

Now, the reason you can't get .../myaction/{id} to work is probably because SiteMapNode is not aware of your data structure - only of the Routes in your application. If they made it work in http://mvcsitemap.codeplex.com/ perhaps it worths a look, but my guess is they didn't. You'll have to populate that area yourself (e.g. for each node which has a parameter, check what node it is and load all the possible values from your data store). And again - pass objects back from your helper class, not HTML.

synhershko
MvcSiteMap does work, but only against non-area-based applications. The MvcSiteMap was never designed, nor never knew of areas as it's a third-party library developed by someone else. The code may be a bit off track, but I actually took that from Microsoft's MVC example for generating a sitemap.
Kezzer
This was just my 2 cents on how things should be done correctly with MVC. Read ScottGu's blog entries on that if you're not familiar enough with the concepts.
synhershko
A: 

It is time to rejoice: Change Set 24979 implements area support.

"Defining sitemap nodes"

...

area | Optional | Area of the specified controller the node should link to

...

ricardo
I was the one who requested it ;)
Kezzer