What?
SharePoint 2010 has a limitation that it only shows up to 2 levels of navigation items at any given time in any page in the current navigation (aka current navigation).
Let us take a typical scenario where I have my site structure as defined below:
Root site
Team Site
Subsite Level 1
Subsite Level 2
Subsite Level 3
The above requirement is typical for especially public/internet facing sites where it is useful to show the structure of your sites/pages exactly in the manner above.
Why?
The below screenshot shows an example of what is expected in an easily navigable public facing SharePoint 2010 site.
The limited OOB behavior in SharePoint 2010 does not allow us to show navigation the way we want through UI settings.
Here is a series of screen shots that I have taken from an OOB publishing site in SharePoint 2010.
Let us take a scenario where I have my site structure as defined below:
Root site
Team Site
Subsite Level 1
Subsite Level 2
Subsite Level 2 Page
Subsite Level 3
Subsite Level 3
Here is what SharePoint 2010 gave me.
The root site:
Subsite Level 1:
Subsite Level 2:
Subsite Level 3:
As you may have noticed, SharePoint does not show any more than 2 levels at any time.
How?
With a combination of little bit of jQuery, CSS & a Custom Control, this can be accomplished easily.
The control that I am going to demonstrate in this post can handle any number of levels to display.
The Custom Control:
Create a new custom control that inherits System.Web.UI.WebControls.HierarchicalDataBoundControl
Use the code provided to build the control:
When the control is compiled, deployed and is ready to be accessed by SharePoint, we need to make sure that it is registered in the master page.
Locate the ContentPlaceHolder with id "PlaceHolderLeftNavBar" and replace its content as shown below:
The CSS:
The jQuery/JavaScript Code:
Make sure that you reference the CSS & jQuery/JavaScript code from your master page properly.
If every thing is done properly, you can expect to see multiple levels of navigation in your current navigation control as shown below:
Thanks to Scott Tindall for all the hard work he put for building the custom control.
SharePoint 2010 has a limitation that it only shows up to 2 levels of navigation items at any given time in any page in the current navigation (aka current navigation).
Let us take a typical scenario where I have my site structure as defined below:
Root site
Team Site
Subsite Level 1
Subsite Level 2
Subsite Level 3
The above requirement is typical for especially public/internet facing sites where it is useful to show the structure of your sites/pages exactly in the manner above.
Why?
The below screenshot shows an example of what is expected in an easily navigable public facing SharePoint 2010 site.
The limited OOB behavior in SharePoint 2010 does not allow us to show navigation the way we want through UI settings.
Here is a series of screen shots that I have taken from an OOB publishing site in SharePoint 2010.
Let us take a scenario where I have my site structure as defined below:
Root site
Team Site
Subsite Level 1
Subsite Level 2
Subsite Level 2 Page
Subsite Level 3
Subsite Level 3
Here is what SharePoint 2010 gave me.
The root site:
Subsite Level 1:
Subsite Level 2:
Subsite Level 3:
As you may have noticed, SharePoint does not show any more than 2 levels at any time.
How?
With a combination of little bit of jQuery, CSS & a Custom Control, this can be accomplished easily.
The control that I am going to demonstrate in this post can handle any number of levels to display.
The Custom Control:
Create a new custom control that inherits System.Web.UI.WebControls.HierarchicalDataBoundControl
Use the code provided to build the control:
using System; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using Microsoft.SharePoint.WebControls; namespace SharePoint2010.Navigation.Current { [ParseChildren(false), PersistChildren(true)] public class LeftNavController : System.Web.UI.WebControls.HierarchicalDataBoundControl { #region Private Members private int currentNodeLevel = 0; private SiteMapNode currentNode; #endregion #region Overrides /// <summary> /// Allows for debugging information to be written to the page source. /// </summary> /// <param name="writer"></param> protected override void Render(HtmlTextWriter writer) { base.Render(writer); if (Page.ClientQueryString.Contains("show_nav_debug")) { writer.Write(string.Format("<!--Current Level:{0} Current Key:{1}-->", currentNodeLevel, currentNode.Key)); } } /// <summary> /// Dynamically set properties of the AspMenu control based on the current location. /// </summary> /// <param name="e"></param> protected override void OnInit(EventArgs e) { base.OnInit(e); SiteMapDataSource dataSource; try { // Find AspMenu controls in the child collection foreach (Control c in this.Controls) { // Check child for proper type AspMenu menu = c as AspMenu; // Continue when child is correct type if (menu != null) { // Extract AspMenu's data source this.DataSourceID = menu.DataSourceID; // Check data source for proper type dataSource = this.GetDataSource() as SiteMapDataSource; // Continue when data source is proper type if (dataSource != null) { // Get current site node currentNode = dataSource.Provider.CurrentNode; // Determine level of current site node currentNodeLevel = DetermineLevel(currentNode, ref currentNodeLevel); // Set properties of the AspMenu and it's data source dataSource.StartFromCurrentNode = true; dataSource.StartingNodeUrl = ""; dataSource.ShowStartingNode = false; menu.MaximumDynamicDisplayLevels = 0; if (currentNodeLevel <= 2) { // Show only children in navigation dataSource.StartingNodeOffset = 0; menu.StaticDisplayLevels = 2; } if (currentNodeLevel == 3) { // Show children and siblings in navigation dataSource.StartingNodeOffset = -2; menu.StaticDisplayLevels = 3; } if (currentNodeLevel == 4) { // Show children and siblings in navigation dataSource.StartingNodeOffset = -3; //menu.StaticDisplayLevels = 4; menu.StaticDisplayLevels = 10; } if (currentNodeLevel == 5) { // Show children and siblings in navigation dataSource.StartingNodeOffset = -4; menu.StaticDisplayLevels = 10; } if (currentNodeLevel == 6) { // Show children and siblings in navigation dataSource.StartingNodeOffset = -5; menu.StaticDisplayLevels = 10; } if (currentNodeLevel == 7) { // Show children and siblings in navigation dataSource.StartingNodeOffset = -6; menu.StaticDisplayLevels = 10; } if (currentNodeLevel >= 8) { // Show children and siblings in navigation dataSource.StartingNodeOffset = -6; menu.StaticDisplayLevels = 10; } //Treat pages as same level as site if (currentNode.Key.ToUpper().Contains("/PAGES/")) { //if (currentNodeLevel >= 5) if (currentNodeLevel >= 3) dataSource.StartingNodeOffset = dataSource.StartingNodeOffset++; else dataSource.StartingNodeOffset--; //menu.StaticDisplayLevels++; } } } } } catch (Exception ex) { throw; } } #endregion #region Private Methods /// <summary> /// Calculate the depth of a site map node by walking back up the tree. /// </summary> /// <param name="item">The site map node to evaluate</param> /// <param name="level">Level to pass between recursive calls</param> /// <returns>An integer representing the depth of the node</returns> private int DetermineLevel(SiteMapNode item, ref int level) { if (item != null && item.ParentNode != null) { level++; DetermineLevel(item.ParentNode, ref level); } return level; } #endregion } }
When the control is compiled, deployed and is ready to be accessed by SharePoint, we need to make sure that it is registered in the master page.
<%@ Register TagPrefix="CurrentNavController" Namespace="SharePoint2010.Navigation.Current" Assembly="SharePoint2010.Navigation.Current,Version=1.0.0.0, Culture=neutral, PublicKeyToken=xxxxxxxxxxxxxxxx" %>
Locate the ContentPlaceHolder with id "PlaceHolderLeftNavBar" and replace its content as shown below:
<asp:ContentPlaceHolder id="PlaceHolderLeftNavBar" runat="server"> <CurrentNavController:LeftNavController runat="server" ID="lNavController"> <!-- Secondary Navigation Data Source --> <PublishingNavigation:PortalSiteMapDataSource ID="SiteMapDS" runat="server" EnableViewState="false" SiteMapProvider="CurrentNavigation" StartFromCurrentNode="true" StartingNodeOffset="0" ShowStartingNode="false" TrimNonCurrentTypes="Heading"/> <!-- end Secondary Navigation Data Source --> <!-- Secondary Navigation Menu --> <nav> <SharePoint:AspMenu ID="CurrentNav" EncodeTitle="false" runat="server" EnableViewState="false" DataSourceID="SiteMapDS" UseSeparateCSS="false" UseSimpleRendering="true" Orientation="Vertical" StaticDisplayLevels="3" MaximumDynamicDisplayLevels="2" CssClass="s4-ql" SkipLinkText="<%$Resources:cms,masterpages_skiplinktext%>"/> </nav> <!-- end Secondary Navigation Menu --> </CurrentNavController:LeftNavController> </asp:ContentPlaceHolder>
The CSS:
/* Hide all ul.static elements */ .s4-ql ul.static > li.static > ul.static > li.static > ul.static { display:none; } .s4-ql ul.static > li.static > ul.static > li.static > ul.static > li.static > ul.static { display:none; } .s4-ql ul.static > li.static > ul.static > li.static > ul.static > li.static > ul.static > li.static > ul.static { display:none; } .s4-ql ul.static > li.static > ul.static > li.static > ul.static > li.static > ul.static > li.static > ul.static > li.static > ul.static { display:none; } .s4-ql ul.static > li.static > ul.static > li.static > ul.static > li.static > ul.static > li.static > ul.static > li.static > ul.static > li.static > ul.static { display:none; } .s4-ql ul.static > li.static > ul.static > li.static > ul.static > li.static > ul.static > li.static > ul.static > li.static > ul.static > li.static > ul.static > li.static > ul.static { display:none; }
The jQuery/JavaScript Code:
$(document).ready(function(){ HandleCurrentNavigation(); }); //Hide unwanted current navigation items function HandleCurrentNavigation() { $(".s4-ql li.selected").parent().show(); $(".s4-ql li.selected > ul.static").show(); $(".s4-ql li.selected").parent().parent().show(); $(".s4-ql li.selected").parent().parent().parent().show(); $(".s4-ql li.selected").parent().parent().parent().parent().show(); $(".s4-ql li.selected").parent().parent().parent().parent().parent().show(); $(".s4-ql li.selected").parent().parent().parent().parent().parent().parent().show(); $(".s4-ql li.selected").parent().parent().parent().parent().parent().parent().parent().show(); }
Make sure that you reference the CSS & jQuery/JavaScript code from your master page properly.
If every thing is done properly, you can expect to see multiple levels of navigation in your current navigation control as shown below:
Thanks to Scott Tindall for all the hard work he put for building the custom control.
Thanks a lot! that's exactly what I'm looking for!
ReplyDeletenice work, thanks!
ReplyDeleteHi,
ReplyDeleteI'm not understand your approach...maybe i miss something...For instance you can get more than 2 levels displayed on Left Navigation by simply modifying these properties StaticDisplayLevels, MaximumDynamicDisplayLevels of Sharepoint:AspMenu used by quicklaunch
Thank you for the great post, but could i kindly ask you where should i place the css,jquery file in the same directory of the custom dll registred? many thanks
ReplyDeleteNo. You can store the css, jquery files anywhere in the site. For example in the Style Library of the root site. Then add references to the js and css files in your master page.
ReplyDeleteThank you for your quick response, where should i write the C# code?
ReplyDeleteMy though is to create a user control (MainPage.ascx) then paste your C# code, then create a class as following calling the user control.
using System;
using System.Collections.Generic;
using System.Text;
using System.Web.UI;
using System.Web.UI.WebControls;
namespace WBOLibraryDLL
{
public class WBOLibraryDLL : System.Web.UI.WebControls.WebParts.WebPart
{
Control control;
protected override void CreateChildControls()
{
this.control = this.Page.LoadControl("~/WBOLibrary/MainPage.ascx");
this.Controls.Add(this.control);
}
}
}
then implement the dll into web.config then site actions - webpart - galleries then i found the new webpart installed.
Please let me know if the above steps going right to proceed?
Many thanks
The custom navigation must inherit from System.Web.UI.WebControls.HierarchicalDataBoundControl and after it is deployed, you must replace the OOB navigation control in SharePoint's master page with this new navigation control as outlined in my post.
ReplyDeleteHope that this helps
Yes, but i should have to create usercontrol inherit from System.Web.UI.WebControls.HierarchicalDataBoundControl and paste your code inside?
ReplyDeleteNo.. You need to create a custom web control (composite control) not a user control
ReplyDeleteHi Chaitu,
ReplyDeleteWhere would you go to add the headings and names for the different levels? Would it be in the normal place Site Settings -> Navigation or do we have to do it with code?
Thanks!!
Just go to site settings > navigation and set up headings, sub-headings and links there
DeleteWhy write a custom control when you can
ReplyDelete"I'm not understand your approach...maybe i miss something...For instance you can get more than 2 levels displayed on Left Navigation by simply modifying these properties StaticDisplayLevels, MaximumDynamicDisplayLevels of Sharepoint:AspMenu used by quicklaunch"