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:
We use the SiteMapNavigationMenu mode of our navigation control. This is a separate class, which we reference with the following TagPrefix:
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.
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.
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.
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.
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).
MegaMenu with RadSiteMap
MegaMenu with Images and description
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:
And here's the ItemTemplateRadItem.ascx (which is my ItemTemplate):
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: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"
/>
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!
Hope they help you create your own Sitefinity MegaMenu!
MegaMenu with RadSiteMap
MegaMenu with Images and description
Zheyna Peleva
Jen Peleva was a Principal frontend developer for the Sitefinity CMS.