|
If you are a SharePoint developer, then you will be in need to customize the default SharePoint menu control. This new feature in MOSS 2007 is great and it allows for suitable level of customization, but unfortunately, this level is not enough for such portal which I’m involved in these days.
I’ve decided to customize the default menu instead of developing new control. To realize the problem suppose that you want your menu to have the following structure:
Site1 Site2 Site3 Site4 …..
Site1Child1 . Site1Child2 . Site1Child3…
As you see, every static item has its own style, also it should has its own hover style, and dynamic items should appear horizontally in case of mouse over the static item. If you edited your master page by sharepoint designer, you will notice that the default SharePoint menu allows you to define only one class for all static items, so we need to edit the static menu template, but even though, the template allows you only for editing "link"s' inner HTML, however you can add another link inside this template, it is a good idea for applying deferent classes:
<StaticItemTemplate> <div id="Static" runat="Server" class='<%# GetStaticClass()%>' onmouseover='<%# GetShowFunction()%>' onmouseout='<%# GetHideFunction()%>'> <asp:hyperLink runat="Server" id="urlNavS" NavigateUrl='<%# Eval("DataPath")%>' Text='<%# Eval("Text")%>' /> </div> <div id='<%# GetDynamicID()%>' class='dynPanel' onmouseover='<%# GetShowFunction()%>' onmouseout='<%# GetHideFunction()%>'></div> </StaticItemTemplate>
You will notice that I’ve put all classes and javascript functions to be driven from C# script, but first you should be permitted from SharePoint to execute C# script in SharePoint pages. To do that you need first to add the following section under <SafeMode> tag in web.config:
<PageParserPaths> <PageParserPath VirtualPath="/_catalogs/masterpage/*" CompilationMode="Always" AllowServerSideScript="true" /> </PageParserPaths>
I’ve written C# script for providing class name for each item and JavaScript functions. That will solve the first problem, but further more we need to display dynamic items horizontally, I think this hard to be done by the default dynamic template, so I’ve assigned a class to hide the default dynamic items and I’ve began to generate them by C# code using OnDataBound event. Here is the full code:
<div id="MenuDiv"> <SharePoint:AspMenu ID="GlobalNav" Runat="server" DataSourceID="SiteMapDataSource1" Orientation="Horizontal" StaticDisplayLevels="1" MaximumDynamicDisplayLevels="1" StaticSubMenuIndent="2" DynamicHorizontalOffset="0" DynamicVerticalOffset="4" StaticEnableDefaultPopOutImage="false" ItemWrap="false" Width="0%" OnDataBound="MenuDataBound"> <StaticMenuItemStyle CssClass="" ItemSpacing="4px" /> <StaticSelectedStyle CssClass="" ItemSpacing="4px" /> <DynamicMenuItemStyle CssClass="dynamicItems" /> <StaticItemTemplate> <div id="Static" runat="Server" class='<%# GetStaticClass()%>' onmouseover='<%# GetShowFunction()%>' onmouseout='<%# GetHideFunction()%>'> <asp:hyperLink runat="Server" id="urlNavS" NavigateUrl='<%# Eval("DataPath")%>' Text='<%# Eval("Text")%>' /> </div> <div id='<%# GetDynamicID()%>' class='dynPanel' onmouseover='<%# GetShowFunction()%>' onmouseout='<%# GetHideFunction()%>'></div> </StaticItemTemplate> <StaticHoverStyle CssClass="topNavHover"/> </SharePoint:AspMenu> <PublishingNavigation:PortalSiteMapDataSource ID="siteMapDataSource1" Runat="server" SiteMapProvider="CombinedNavSiteMapProvider" EnableViewState="true" StartFromCurrentNode="true" StartingNodeOffset="0" ShowStartingNode="false" TreatStartingNodeAsCurrent="true" TrimNonCurrentTypes="Heading"/> <div id="dynamicItems" runat="server"></div>
<script runat="server"> int stCount = 0; protected string GetStaticClass() { stCount += 1; return "staticItem" + stCount .ToString() ; } protected string GetDynamicID() { return "DynamicItem" + stCount .ToString() ; } protected string GetShowFunction() { return "showDynamicItems(" + stCount .ToString() + ")" ; } protected string GetHideFunction() { return "hideDynamicItems(" + stCount .ToString() + ")" ; } protected void MenuDataBound(object sender, EventArgs e) { int ItemIndex = 0; int subIndex = 0; string divContent = ""; foreach(System.Web.UI.WebControls.MenuItem item in GlobalNav.Items) { subIndex = 0; foreach(System.Web.UI.WebControls.MenuItem cItem in item.ChildItems ) { if(subIndex == 0) { divContent += "<div style='display:none' id='subItems" + ItemIndex.ToString() + "'><table class='MenuSubDiv" + (ItemIndex + 1).ToString() + "' ><tr>"; } divContent += "<td><a href='" + cItem.DataPath + "'>" + cItem.Text + "</a></td>"; if(subIndex == item.ChildItems.Count-1) { divContent += "</tr></table></div>"; } else { divContent += "<td> <img src='/_layouts/images/menu_sub_bullet.gif'/> </td>"; } subIndex += 1; } ItemIndex += 1; } dynamicItems.InnerHtml += divContent ; } </script> <script language="javascript" type="text/javascript"> function showDynamicItems(num) { try { var dv = document.getElementById("subItems" + (num -1).toString()); var dynCont = document.getElementById("DynamicItem" + num.toString()); if (dynCont.style.display!="block") { hideAll(); dynCont.innerHTML = dv.innerHTML ; dynCont.style.display = "block" ; } } catch (e) { } } function hideAll() { var count =1; while(true) { try { var dynCont = document.getElementById("DynamicItem" + count.toString()); if(dynCont.style.display != "none") { dynCont.style.display = "none" ; } count += 1; } catch(e) { return; } } } function hideDynamicItems(num) { try { var dynCont = document.getElementById("DynamicItem" + num.toString()); dynCont.style.display = "none" ; } catch (e) { } } </script> </div>
[Update April, 11, 2009]
I'm so sorry for being so busy last year, I'm including the CSS file here for all people whom requested it.
|