How to Create a MegaMenu in Sitefinity

How to Create a MegaMenu in Sitefinity

Posted on June 21, 2013 0 Comments

The content you're reading is getting on in years
This post is on the older side and its content may be out of date.
Be sure to visit our blogs homepage for our latest news, updates and information.

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. MegaMenu 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 description 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, demonstrating 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 
Zheyna Peleva headshot

Zheyna Peleva

Jen Peleva was a Principal frontend developer for the Sitefinity CMS.

Comments

Comments are disabled in preview mode.
Topics

Sitefinity Training and Certification Now Available.

Let our experts teach you how to use Sitefinity's best-in-class features to deliver compelling digital experiences.

Learn More
Latest Stories
in Your Inbox

Subscribe to get all the news, info and tutorials you need to build better business apps and sites

Loading animation