mvcsitemapproviderasp.net-mvc-sitemap

How to create a new model?


I wish I could do the following: @Html.MvcSiteMap().PageMenu() instead of @Html.MvcSiteMap().Menu("MenuBtnGroup").

Mainly because of the filters.

I wish that a certain link to appear only on the page or sitemap and not in the main application menu

<mvcSiteMapNode title="Usuários" controller="Usuarios" action="Index">
    <mvcSiteMapNode title="Novo Usuário" action="Novo" visibility="SiteMapPathHelper,PAGEMENU-ONLY,!*" />
    <mvcSiteMapNode title="Detalhes" action="Detalhes" visibility="SiteMapPathHelper,!*" dynamicNodeProvider="UsuarioDynamicNodeProvider, Web">
        <mvcSiteMapNode title="Editar" action="Editar" inheritedRouteParameters="id" />
    </mvcSiteMapNode>
</mvcSiteMapNode>

Solution

  • This functionality isn't built-in (yet), but there is a way to make it happen by building your own visibility provider and using the SourceMetaData to pass the name of the menu into the visibility logic.

    /// <summary>
    /// Filtered SiteMapNode Visibility Provider for use with named controls.
    /// 
    /// Rules are parsed left-to-right, first match wins. Asterisk can be used to match any control or any control name. Exclamation mark can be used to negate a match.
    /// </summary>
    public class CustomFilteredSiteMapNodeVisibilityProvider
        : SiteMapNodeVisibilityProviderBase
    {
        #region ISiteMapNodeVisibilityProvider Members
    
        /// <summary>
        /// Determines whether the node is visible.
        /// </summary>
        /// <param name="node">The node.</param>
        /// <param name="sourceMetadata">The source metadata.</param>
        /// <returns>
        ///     <c>true</c> if the specified node is visible; otherwise, <c>false</c>.
        /// </returns>
        public override bool IsVisible(ISiteMapNode node, IDictionary<string, object> sourceMetadata)
        {
            // Is a visibility attribute specified?
            string visibility = string.Empty;
            if (node.Attributes.ContainsKey("visibility"))
            {
                visibility = node.Attributes["visibility"].GetType().Equals(typeof(string)) ? node.Attributes["visibility"].ToString() : string.Empty;
            }
            if (string.IsNullOrEmpty(visibility))
            {
                return true;
            }
            visibility = visibility.Trim();
    
            // Check for the source HtmlHelper
            if (sourceMetadata["HtmlHelper"] == null)
            {
                return true;
            }
            string htmlHelper = sourceMetadata["HtmlHelper"].ToString();
            htmlHelper = htmlHelper.Substring(htmlHelper.LastIndexOf(".") + 1);
    
            string name = sourceMetadata["name"].ToString();
    
            // All set. Now parse the visibility variable.
            foreach (string visibilityKeyword in visibility.Split(new[] { ',', ';' }))
            {
                if (visibilityKeyword == htmlHelper || visibilityKeyword == name || visibilityKeyword == "*")
                {
                    return true;
                }
                else if (visibilityKeyword == "!" + htmlHelper || visibilityKeyword == "!" + name || visibilityKeyword == "!*")
                {
                    return false;
                }
            }
    
            // Still nothing? Then it's OK!
            return true;
        }
    
        #endregion
    }
    

    Then you can just name each of your menus by giving them a "name" SourceMetadata attribute.

    @Html.MvcSiteMap().Menu(new { name = "MainMenu" })
    @Html.MvcSiteMap().Menu(new { name = "PageMenu" })
    

    And then use the CustomFilteredSiteMapVisibilityProvider instead of the FilteredVisibilityProvider in your configuration. See this answer for a complete example.