This is a migrated thread and some comments may be shown as answers.

One sitemap different menus

6 Answers 125 Views
Menu
This is a migrated thread and some comments may be shown as answers.
This question is locked. New answers and comments are not allowed.
Andy
Top achievements
Rank 1
Veteran
Andy asked on 10 Aug 2010, 06:43 PM
Hi,

I want to have a main site menu that only has the top level (area) shown and an area menu that only has the current area's menu items on it.  

I have created a complete web.sitemap that has everything but cannot find a method so that only the top level is displayed. In addition, I can't get the area menu to start at the appropriate level.   Is this possible?  Or do I need to have a site map for every area and use logic or different master pages to get the various menus?

Andy

6 Answers, 1 is accepted

Sort by
0
Rich H
Top achievements
Rank 1
answered on 11 Aug 2010, 09:27 AM
Hi Andy

I'm not sure if you can do this from just one site map but if there is a way I suspect it would involve some coding logic during the site map rendering process.

If I understand what you are trying to do correctly, I think I would go down the route of having a master page at the site level with a menu control that binds to a site map at the same level (which just contains nodes for each of your Areas) and then have a master page in each Area containing a separate menu control that binds to an Area specific site map. There may be other ways to do this but IMHO I think this is probably the simplest.

Hope this helps,

-Rich
0
Andy
Top achievements
Rank 1
Veteran
answered on 11 Aug 2010, 12:44 PM
Hi RIch,

We're on the same wavelength.  I indeed have a site master with the top level menu and a nested master for each area with the area menu/sitemap in each.    

Today's though was to have my site master and a partial view for each section with it's own menu and use simple logic based on the area to render the correct partial view.

I think I'm going to need to go for clunky implementation over elegance :-(

Andy
0
Rich H
Top achievements
Rank 1
answered on 12 Aug 2010, 03:37 PM
Hi Andy

Your last post got me thinking and there is a way for you to achieve what you want using one site master, one partial view (to render the menu) and several site maps, one for the main site and the others relating to each Area.

First, create the site maps and then load them in the application_onstart() method of your Global.ascx class as in the following example:

SiteMapManager.SiteMaps.Register<XmlSiteMap>("Main", sitmap => sitmap.Load());
  
SiteMapManager.SiteMaps.Register<XmlSiteMap>("AreaOne", sitmap => sitmap.LoadFrom("~/Areas/TestOne/AreaOne.sitemap"));
  
SiteMapManager.SiteMaps.Register<XmlSiteMap>("AreaTwo", sitmap => sitmap.LoadFrom("~/Areas/TestTwo/AreaTwo.sitemap"));

Then populate each controller with the PopulateSiteMap attribute: The site map name parameter should correspond to the correct map for the area the controller is in (as loaded in the global.ascx class) and the view data key parameter should be the same for each:

[PopulateSiteMap(SiteMapName="AreaOne", ViewDataKey="MenuItems")]
   public class AreaOneController : Controller
  
[PopulateSiteMap(SiteMapName="AreaTwo", ViewDataKey="MenuItems")]
   public class AreaTwoController : Controller
  
[PopulateSiteMap(SiteMapName="Main", ViewDataKey="MenuItems")]
   public class MainSiteController : Controller

You could even create a base controller class for each Area with the attribute populated, which all other controllers for the Area are derived from.

Then create a partial view which renders the menu, binding it to the ViewData key containing the menu data:

<%
   Html.Telerik().Menu().Name("MainMenu").BindTo("MenuItems").Render();
%>

From the site master all you need to do then is pass the view data from the current context to the call to render the partial view (assuming the name of the partial view is ShowMenu.ascx):

<% Html.RenderPartial("ShowMenu", ViewData); %>

This results in the correct menu for each Area being rendered. I've tested this and it works - not sure if it is applicable to your situation but it seems a fairly straightforward way to do this with minimum code.

Hope this helps, but if you come up with something different please post what you have done as I would be interested to know the final outcome :)

Rich


 
0
Andy
Top achievements
Rank 1
Veteran
answered on 13 Aug 2010, 09:15 AM
Hi Rich,

That looks just like the solution I'm after and has a nice elegance to it.  I'm probably going to go with it.

Thanks,
Andy :-)
0
Andy
Top achievements
Rank 1
Veteran
answered on 15 Aug 2010, 04:36 PM
I spent a few hours this weekend getting the kind of implementation I want.  I've got two areas - Area1 and Area2.  The main sitemap is web.sitemap and I load it in Global.ascx.cs using SiteMapManager.SiteMaps.Register<XmlSiteMap>("MainMenu", sitemap=>sitemap.Load());

In each area I create the area specific sitemap - ~/Area1/web.sitemap and ~/Area2/web.sitemap.   I wanted to load these using a base controller class but I couldn't find a method to get the area as the RouteData and other collections were all null.   Instead I created a filter attribute to decorate my controllers (or base controller).  This attribute is similiar to the Telerik PopulateSiteMapAttribute except that it looks for an area in the route and if found loads the area sitemap and adds it to viewdata. 

 

#region Comment
/// <summary>
/// Represents an attribute that is used to populate a <see cref="Telerik.Web.Mvc.SiteMapBase"/>
/// in view data.
/// </summary>
#endregion
[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public class AreaSiteMapAttribute: FilterAttribute, IActionFilter
{
    private string _siteMapName = "web.sitemap";
    private string _viewDataKey = "siteMap";
    #region Comment
    /// <summary>
    /// Gets/sets the name of the .sitemap file to use
    /// </summary>
    /// <remarks>If this property is not set if will default to "web.sitemap" is used. This file
    /// must exist within the project at "~/Areas/area".</remarks>
    #endregion
    public string SiteMapName
    {
        get { return _siteMapName; }
        set { _siteMapName = value; }
    }
    #region Comment
    /// <summary>
    /// Gets/sets the <see cref="ViewData" key that will be used to store the
    /// <see cref="Telerik.Web.Mvc.SiteMap"/>./>
    /// </summary>
    /// <remarks>If this property is not set if will default to "siteMap".</remarks>
    #endregion
    public string ViewDataKey
    {
        get { return _viewDataKey; }
        set { _viewDataKey = value; }
    }
    #region IActionFilter Members
    #region Comment
    /// <inheritdoc/>
    #endregion
    public void OnActionExecuted(ActionExecutedContext filterContext)
    {
    }
    #region Comment
    /// <inheritdoc/>
    #endregion
    public void OnActionExecuting(ActionExecutingContext filterContext)
    {
        string area = null;
        // Try to find the area
        if (filterContext.RouteData.DataTokens.ContainsKey("area"))
        {
            area = (string)filterContext.RouteData.DataTokens["area"];
        }
        if (area != null)
        {
            // Load the area's sitemap if it not already loaded
            if (!SiteMapManager.SiteMaps.ContainsKey(area))
            {
                SiteMapManager.SiteMaps.Register<XmlSiteMap>(area, sitemap => sitemap.LoadFrom(string.Format("~/Areas/{0}/{1}", area, _siteMapName)));
            }
            // Fetch the site map and store in in ViewData
            var siteMap = (XmlSiteMap)SiteMapManager.SiteMaps[area];
            filterContext.Controller.ViewData[_viewDataKey] = siteMap;
        }
    }
    #endregion
}

0
Rich H
Top achievements
Rank 1
answered on 19 Aug 2010, 04:19 PM
Hi Andy

Thanks for sharing your solution; I've found this very useful.

Cheers,

Rich
Tags
Menu
Asked by
Andy
Top achievements
Rank 1
Veteran
Answers by
Rich H
Top achievements
Rank 1
Andy
Top achievements
Rank 1
Veteran
Share this question
or