First we need to create two lists in SharePoint. The list names I used were “WSSTopNavigation” and “WSSDropDownNavigation”
The structure of WSSTopNavigation looks like the following:

WSSDropDownNavigation is very similar with the execption of it looks up a category from WSSTopNavigation as its link.

The SiteMapProvider can be the tricky part. Create a new project in Visual Studio. The first step is to create a project with a strong name key file and the references diagramed below:

Now create a class file named MenuProvider.cs. The following using statement will be helpful along the way.
using Microsoft.SharePoint;
using Microsoft.SharePoint.WebControls;
using System.Collections;
using Microsoft.SharePoint.Navigation;
The class will inherit from the SPNavigationProvider. This will allow us to simply the process quite a bit.
The first thing we want to do is override the GetChildNodes. I only wanted to modify the “Home” node for my solution. One thing to note is that the current nodes created by SharePoint are SPSiteMapNodes. This is internal and presents a small problem. Our new node is of the SiteMapNode. You will need to trap an type error when the GetChildNodes method executes against your new nodes. I trapped this below and returned null.
-------------------------------------------------------------------------------
public override SiteMapNodeCollection GetChildNodes(SiteMapNode node)
{
SiteMapNodeCollection nodeCollection;
try
{
nodeCollection = base.GetChildNodes(node);
if (node.Title == "Home")
{
SiteMapNodeCollection newNodes = GetNodesFromSharePointList(node);
nodeCollection = newNodes;
}
}
catch
{
nodeCollection = null;
}
return nodeCollection;
}
-------------------------------------------------------------------------------
Now all that is needed to complete the class file is to write the GetNodesFromSharePointList method. All I am doing here is building out a SiteMapNodeCollection from the two lists we created in the first step and use CAML to pull them out in position order and to tie the sub-menus to the parents.
-------------------------------------------------------------------------------
private SiteMapNodeCollection GetNodesFromSharePointList(SiteMapNode node)
{
SiteMapNodeCollection wssListNode;
try
{
wssListNode = base.GetChildNodes(node);
}
catch
{
wssListNode = new SiteMapNodeCollection();
}
SPWeb currentWeb = SPContext.Current.Web;
string listName = "WSSTopNavigation";
string secondName = "WSSDropDownNavigation";
if (listName != null && listName.Length > 0)
{
try
{
//Title Single line of text
//Category Single line of text
//Position Number
//NavigationURL Single line of text
//DisplayName Single line of text
//Created By Person or Group
//Modified By Person or Group
SPListItemCollection categoryItems;
SPList categoryList = currentWeb.Lists[listName];
SPList secondList = currentWeb.Lists[secondName];
SPQuery posQuery = new SPQuery();
posQuery.Query = " ";
categoryItems = categoryList.GetItems(posQuery);
for (int i = 0; i < nodeurl =" categoryItems[i][" nodetitle =" categoryItems[i][" nodedescr =" categoryItems[i][" secquery =" new" query =" string.Format(">{0} ", categoryItems[i]["Category"].ToString());
secItems = secondList.GetItems(secQuery);
SiteMapNode newNode = new SiteMapNode(node.Provider, "/", nodeUrl, nodeTitle, nodeDescr);
newNode.ChildNodes = new SiteMapNodeCollection();
for (int ii = 0; ii < nodesecurl =" secItems[ii][" nodesectitle =" secItems[ii][" nodesecdescr =" secItems[ii][" secnode =" new">
{
SiteMapNodeCollection wssListNode;
try
{
wssListNode = base.GetChildNodes(node);
}
catch
{
wssListNode = new SiteMapNodeCollection();
}
SPWeb currentWeb = SPContext.Current.Web;
string listName = "WSSTopNavigation";
string secondName = "WSSDropDownNavigation";
if (listName != null && listName.Length > 0)
{
try
{
//Title Single line of text
//Category Single line of text
//Position Number
//NavigationURL Single line of text
//DisplayName Single line of text
//Created By Person or Group
//Modified By Person or Group
SPListItemCollection categoryItems;
SPList categoryList = currentWeb.Lists[listName];
SPList secondList = currentWeb.Lists[secondName];
SPQuery posQuery = new SPQuery();
posQuery.Query = "
categoryItems = categoryList.GetItems(posQuery);
for (int i = 0; i < nodeurl =" categoryItems[i][" nodetitle =" categoryItems[i][" nodedescr =" categoryItems[i][" secquery =" new" query =" string.Format(">
secItems = secondList.GetItems(secQuery);
SiteMapNode newNode = new SiteMapNode(node.Provider, "/", nodeUrl, nodeTitle, nodeDescr);
newNode.ChildNodes = new SiteMapNodeCollection();
for (int ii = 0; ii < nodesecurl =" secItems[ii][" nodesectitle =" secItems[ii][" nodesecdescr =" secItems[ii][" secnode =" new">
-------------------------------------------------------------------------------
You can now compile your code and either GAC (global assembly cache) your DLL or stick it in the Bin file of your web application.
Now open up the web.config file in the web application folder. Find the SiteMap section and add your new provider.
You can now compile your code and either GAC (global assembly cache) your DLL or stick it in the Bin file of your web application.
Now open up the web.config file in the web application folder. Find the SiteMap section and add your new provider.

This bring us to our final step. You have to change the SiteMapProvider in your master page. It should look like the following:
Get the source code here.




5 comments:
Hi Nice article,
I have scenario like this.
I have created subsites site1, site2, site3 in a site collection.
Every site has their own pages document library.Depend on some scenario some time I want to select site1,site2 and in other scenario site1,site3 only.
1.How can we use the inbuilt pages document library within the site to create the menu instead of a custom list?
2.How can we iterate through the nested subsites and their respective page document library?
I've completed every step, but nothing happened. The wss website shows the top link bar, not the custom list navigation.
I also put some items in the WSSTopNavigation list.
What am I doing wrong??
Hi,
I'm trying to use the code to customize my navigation with Features. If I try it my feature, I'm getting the following error, which is really strange
Failed to create a control from assembly 'ShannonBray.WSS.MenuProvider, version=1.0.0.0, Culture=neutral, PublicKeyToken=9f2617b5eee77523', class 'ShannonBray.WSS.MenuProvider.MenuProvider': Unable to cast object of type 'ShannonBray.WSS.MenuProvider.MenuProvider' to type 'System.Web.UI.Control'.
DelegateControl: Exception thrown while building custom control 'Microsoft.SharePoint.SPControlElement': System.InvalidCastException: Unable to cast object of type 'ShannonBray.WSS.MenuProvider.MenuProvider' to type 'System.Web.UI.Control'. at Microsoft.SharePoint.Utilities.SPUtility.CreateServerControlFromAssembly(String sControlAssembly, String sControlClass) at Microsoft.SharePoint.SPControlElement.BuildCustomControl(TemplateControl tctlPage, String sControlAssembly, String sControlClass, String sControlSrc, XmlNode xnElementDefinition, SPFeatureDefinition featdefElement, String sElementId) at Microsoft.SharePoint.SPControlElement.BuildCustomControl(TemplateControl tctlPage) at Microsoft.SharePoint.WebControls.DelegateControl.BuildCustomControlResilient(SPControlElement ctlelemDefinition)
If I remove the Delegate control and set the datasource manually in the code from masterpage, it works great.
Any idea why this might happen. I'm trying to fix this for some time and it really runs me nuts
I tried running this in few servers and getting the same message.
Any help will be greatly appreciated.
Hi
I see this solution to be working except it doesn't take care of security triming;
meaning if i have Site Collection A / Site Collection B / Site Collection C. I want to show Menu with these three link in the Top Navigation.
I have a user A who has access to only site collection A / site Collection B but not Site Collection C.
when user access the site he will see all the three links; and when clicks on site collection C link he will get access deneid instead i wanted to check with you if you have way of finding out login user context and apply the security on the menu and show / hide these links....
Pls Let me know if you have any suggestion to the scenario i mentioned
Thanking you in advance
-Praveen
Praveen,
I used the following code to check on the URL to see whether the user has access on or not and the code runs under the Users security context.
private bool CheckAccess ( string url )
{
bool retValue = false;
object spfile1 = null;
SPSite spSite = null;
SPWeb spweb = null;
try
{
spSite = new SPSite(url);
spSite.CatchAccessDeniedException = false;
string file = url.Replace(spSite.Url, "");
try
{
spfile1 = spSite.RootWeb.GetFileOrFolderObject(file);
retValue = true;
}
catch (ThreadAbortException ex)
{
Thread.ResetAbort();
}
catch (Exception ex)
{
}
if (!retValue)
{
spweb = spSite.OpenWeb(file, false);
spfile1 = spweb.GetFileOrFolderObject(file);
if ((spfile1 is SPFile) || (spfile1 is SPFolder))
{
retValue = true;
}
}
}
catch (ThreadAbortException ex)
{
Thread.ResetAbort();
}
catch (Exception ex)
{
}
finally
{
if (spSite != null)
{
spSite.CatchAccessDeniedException = true;
spSite.Dispose();
spSite = null;
}
if (spweb != null)
{
spweb.Dispose();
spSite = null;
}
}
return retValue;
}
And by the way, did you by any chance, you're using Feature to activate the Custom Navigation. If yes, I would appreciate, if you could send me the code @ malathyr at gmail dot com
Post a Comment