+1-888-365-2779
Try Now
More in this section
Categories
Bloggers
Blogs RSS feed

How to Create a MegaMenu in Sitefinity

by Zheyna Peleva
Since many of you are interested in creating MegaMenu navigations with Sitefinity I decided to write a blog post about it. In this blog post I'm going to show you how to create two types of MegaMenu navigations. The first one is a dropdown navigation, where the direct children of the first level items of the navigation contain another navigation control (RadSiteMap), configured to show all child pages under the first level parent. The second one will allow you to add images and some Page properties to the item. Let's start with:

1. MenagMenu with RadSiteMap:
<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="MagaMenuTemplate.ascx.cs" Inherits="SitefinityWebApp.ControlTemplates22.MegaMenuTemplate"
 
%>
<%@ Register Assembly="Telerik.Sitefinity" Namespace="Telerik.Sitefinity.Web.UI" TagPrefix="sf" %>
<%@ Register Assembly="Telerik.Sitefinity" Namespace="Telerik.Sitefinity.Web.UI.NavigationControls.SiteMapNavigations" TagPrefix="navcontrols" %>
<%@ Register Assembly="Telerik.Web.UI" Namespace="Telerik.Web.UI" TagPrefix="telerik" %>
<%@ Register Assembly="Telerik.Sitefinity" Namespace="Telerik.Sitefinity.Web.UI.NavigationControls" TagPrefix="sfMap" %>
<navcontrols:SiteMapNavigationMenu ID="siteMapControl_horizontaldropdownmenu" runat="server"  Skin="Sitefinity">
 
</navcontrols:SiteMapNavigationMenu>
<script type="text/javascript">
    function radMenuOnClick(sender, args) {
 
        var state = args.get_item().get_attributes().getAttribute("ExpandOnClick");
        args.get_item().get_attributes().setAttribute("ExpandOnClick", "true")
        args.get_item().open();
    }
 
    function radMenuOnOpening(sender, args) {
        var state = args.get_item().get_attributes().getAttribute("ExpandOnClick");
        if (state != "true")
            args.set_cancel(true);
        args.get_item().get_attributes().setAttribute("ExpandOnClick", "false")
    }
 
    $(document).ready(function() {
        var mainNavigation = $(".main-menu .floor2 .menuwrap .RadMenu.RadMenu_Sitefinity .rmRootGroup.rmHorizontal");
        //$(".main-menu .floor2 .menuwrap .RadMenu.RadMenu_Sitefinity .rmRootGroup.rmHorizontal li:nth-child(2) div.rmSlide").addClass("members");
    });
</script>

We use the SiteMapNavigationMenu mode of our navigation control. This is a separate class, which we reference with the following TagPrefix:

<%@ Register Assembly="Telerik.Sitefinity" Namespace="Telerik.Sitefinity.Web.UI.NavigationControls.SiteMapNavigations" TagPrefix="navcontrols" %>

In the code-behind we subscribe to the ItemDataBound event and create a new RadMenuItem, which will hold the RadSiteMap if the currently bound item has children. This radSiteMap will display only its children. In this part of the event we create a new TemplateInfo, which contains the inner template information, or else said - contains the path to the ascx template, which contains the RadSiteMap.

var templateInfo = new TemplateInfo()
               {
                   TemplatePath = "~/ControlTemplates/ItemTemplate.ascx",
                   TemplateName = String.Empty,
                   TemplateResourceInfo = this.ResourcesAssemblyInfo,
                   ControlType = this.GetType(),
                   Key = TemplateKey
 
               };

This template will be then used to build the news RadMenuitem. We get the real template from the templateInfo, using Controlutilities, and instantiate it in an ItemContainer. ItemContainer is a separate class we've created, that inherits from GenericContainer and in it we access the radSiteMap control from the ItemTemplate.ascx, as well as the SiteMapDataSource. Let's take a look at ItemTemplate.ascx. Notice the simple markup, that contains only the SiteMap and its datasource.

<%@ Control Language="C#" AutoEventWireup="true" %>
<telerik:RadSiteMap runat="server" ID="RadSiteMap1" DataSourceID="siteMapDataSource1"></telerik:RadSiteMap>
<asp:SiteMapDataSource runat="server" ID="siteMapDataSource1" />

Now, getting back to the ItemDataBound event we take advantage of the isNodeAccessible event to distinguish which items should appear in the RadSiteMap item navigation.

((SiteMapBase)menuItemContainer.SiteMapDataSourceControl.Provider).IsNodeAccessible += new EventHandler<IsAccessibleArgs>
 (MegaMenuTemplate_IsNodeAccessible);

void MegaMenuTemplate_IsNodeAccessible(object sender, IsAccessibleArgs e)
        {
            var pageNode = e.Node as PageSiteNode;
            if (pageNode != null)
            {
                if (/*!pageNode.ShowInNavigation ||*/
                    /*(!pageNode.IsGroupPage  && pageNode.Hidden ) ||*/
                   (String.IsNullOrEmpty(pageNode.Title) && // used in multilingual; captures the cases of synced, split, and group pages
                       pageNode.Id != SiteInitializer.CurrentFrontendRootNodeId)) // ensures that the root node is not hidden and thus all other pages
                {
                    e.IsAccessible = false;
                     
                    return;
                }
 
                //The page should be hidden if it is a group page and it has no child pages.
                if (pageNode.IsGroupPage && pageNode.ChildNodes.Count < 1)
                {
                    e.IsAccessible = false;
                    return;
                }
 
                if (pageNode.ShowInNavigation == false)
                {
                    e.IsAccessible = false;
                    return;
                }
 
                if (pageNode.Title != "Pages" && pageNode.Status != Telerik.Sitefinity.GenericContent.Model.ContentLifecycleStatus.Live)
                {
                    e.IsAccessible = false;
                    return;
                }
            }
            else
            {
                throw new NotSupportedException("The supported types are 'TaxonSiteNode' or 'PageSiteNode'.");
            }
 
            e.IsAccessible = true;
        }

This way we make sure that only pages, that are published and with ShowInNavigaton, set to true, will appear in the inner navigation (the RadSiteMap). Then we set the StartingNodeUrl of the SiteMapDataSource control from InnerTemplate.ascx to the currently bound page. This way only children of the parent item will appear in the RadSiteMap. So, for example, if you have p1 on first level and p11, p12, p13 are children of p1, they will be displayed by the RadSiteMap from Itemtemplate.ascx, as well as their children. The last thing I do is to hide all items, that aren't first level, from the main SiteMapNavigationMenu navigation. Here's how this works.

2. MegaMenu with Images and page properties in each tab

The second navigation, is based on the first one with some differences. Instead of adding a new RadMenuItem, we add an image, the page desciption and a link to the page to the item on ItemDataBound. Our ItemContainer class this time contains more properties - for each control on our ItemTemplate:

public class ItemContainer : GenericContainer
        {
            public ImageControl ImageControl
            {
                get
                {
                    return this.GetControl<ImageControl>("imageControl", false);
                }
            }
 
            public Label Description
            {
                get
                {
                    return this.GetControl<Label>("description", false);
                }
            }
            public HyperLink ItemUrl
            {
                get
                {
                    return this.GetControl<HyperLink>("itemUrl", false);
                }
            }

 And here's the ItemTemplateRadItem.ascx (which is my ItemTemplate):

<%@ Control Language="C#" AutoEventWireup="true" CodeBehind="ItemTemplateRadItem.ascx.cs" Inherits="SitefinityWebApp.ItemTemplateRadItem" %>
<%@ Register Assembly="Telerik.Sitefinity" Namespace="Telerik.Sitefinity.Web.UI.PublicControls" TagPrefix="sf" %>
<asp:Label runat="server" ID="description" />
<sf:ImageControl runat="server" id="imageControl"/>
<asp:HyperLink runat="server" ID="itemUrl" />

The label will show the page description, the image - an image (with the same title as the page), and the HyperLink - a link to the page. Then we practically access these controls through the properties and assign values to them:

if (image != null)
               {
                   menuItemContainer.ImageControl.ImageId = image.Id;
                   shouldAdd = true;
               }
               if (!description.IsNullOrEmpty())
               {
                   menuItemContainer.Description.Text = description;
                   shouldAdd = true;
               }
               if (shouldAdd)
               {
                   e.Item.Controls.Add(menuItemContainer);
               }

Here's a video, demonstratiing how this approach works. The p21 page has a description and an image (image in the library with title p21 - this is the only way we can retrieve it on the ItemDataBound event).

Below, I have attached both samples.

Hope they help you create your own Sitefinity MegaMenu!

MegaMenu with RadSiteMap

MegaMenu with Images and description 

8 comments

Leave a comment
  1. Steve Jun 21, 2013
    http://www.sitefinitysteve.com/blog/code/2013/05/30/introducing-the-scriptstyle-widget

    <sf:ScriptStyle>
       <script>
         //code goes here
       </script>
    </sf:ScriptStyle>

    It'll render at the bottom of the page instead of right in the template in your menu

    Or probably better yank it out to an external .js ;)
  2. Steve Jun 21, 2013
    Sorry I had more text before the link, but the fantastically bad RadEditor appears to have stripped it all out when I used the link tool...great
  3. Jen Jun 21, 2013
    Hey, Steve!

    Impressive widget! (: I'm just not sure what will get rendered on the bottom on the page?
  4. PHP Development Company Jul 08, 2013
    That was really helpful. I need to get it done for my new web application development. Well Explained....
  5. cnabilou Jul 09, 2013
    Do you have an example that will work in MVC only mode?
  6. Marzia Apr 04, 2014
    Hello, first of all great post, it was super useful, I was wondering if you can help me out with the localization, the menu is working great fro the default language, but once I switch to another language it breaks   here menuItemContainer.SiteMapDataSourceControl.StartingNodeUrl = e.Item.NavigateUrl; and the url with the localization is not found.  Any thoughts on this. Thanks again.
  7. Fred Jun 04, 2014

    Hello great post

    I get a warning saying do not use this method because its going to be removed in future releases for the following line.

    ((SiteMapBase)menuitemcontainer.sitemapdatasourcecontrol.provider).isnodeaccessible

    What should we use instead?

  8. Michelle Jul 18, 2014
    I have the files and this is exactly what I needed.. this may sound like a stupid question because I'm new to sitefinity, but where would I place these files?

    Leave a comment