Allowing the menu helper to exceed maxDepth

Jan 7, 2011 at 4:06 PM

The current MenuHelper is a bit inflexible, returning a model where all branches are of equal depth, but often you want to have the current menu section expanded further. I was wanting to have a menu that was 2 levels deep except for allowing the levels up to the current node to also be included.

I've developed an extra overload onto MenuHelper to allow this but as we are using 2.3.0 I haven't submitted it as a patch. I was looking at porting it over to 3.0.0 but I was wary about polluting the Menu() overloads any more. Apart from the overloads to menu that I added internally the changes made are only adding an extra private method called ContinueBuilding and making a change in BuildModel. As I haven't submitted a patch and the changes are quite small I thought I'd post it in here and if you'd like to include it the choice of which Menu() methods to add the overloads to can be made at a higher level.

The test on maxDepth has been replaced with a call to ContinueBuilding. This tests the maxDepth, then if drillDownToCurrent is true will also return true if:

  • The node is in the current path
  • The node is a direct descendant of the current node
  • One of the node's siblings is in the current path

private static MenuHelperModel BuildModel(MvcSiteMapHtmlHelper helper, SiteMapNode startingNode, bool startingNodeInChildLevel, bool showStartingNode, int maxDepth, bool drillDownToCurrent)
		{
			// Build model
			var model = new MenuHelperModel();
			var node = startingNode;
			var mvcNode = node as MvcSiteMapNode;

			bool continueBuilding = ContinueBuilding(maxDepth, node, mvcNode, drillDownToCurrent);

			// Check if maximal node level has not been reached
			if (!continueBuilding)
			{
				return model;
			}

			// Check visibility
			bool nodeVisible = true;
			if (mvcNode != null)
			{
				nodeVisible = mvcNode.VisibilityProvider.IsVisible(
					node, HttpContext.Current, SourceMetadata);
			}

			// Check ACL
			if (nodeVisible && node.IsAccessibleToUser(HttpContext.Current))
			{
				// Add node?
				var nodeToAdd = SiteMapNodeModelMapper.MapToSiteMapNodeModel(node, mvcNode, SourceMetadata);
				if (showStartingNode || !startingNodeInChildLevel)
				{
					model.Nodes.Add(nodeToAdd);
				}

				// Add child nodes
				if (node.HasChildNodes)
				{
					foreach (SiteMapNode childNode in node.ChildNodes)
					{
						foreach (var childNodeToAdd in BuildModel(helper, childNode, false, true, maxDepth - 1, drillDownToCurrent).Nodes)
						{
							if (!startingNodeInChildLevel)
							{
								nodeToAdd.Children.Add(childNodeToAdd);
							}
							else
							{
								model.Nodes.Add(childNodeToAdd);
							}
						}
					}
				}
			}

			return model;
		}

		private static bool ContinueBuilding(int maxDepth, SiteMapNode node, MvcSiteMapNode mvcNode, bool drillDownToCurrent)
		{
			if (maxDepth > 0)
				return true;
			if (!drillDownToCurrent)
				return false;
			if (node.IsInCurrentPath())
				return true;
			if (node.ParentNode == node.Provider.CurrentNode)
				return true;
			foreach (SiteMapNode sibling in node.ParentNode.ChildNodes)
			{
				if (sibling.IsInCurrentPath())
					return true;
			}
			return false;
		}

Coordinator
Jan 10, 2011 at 12:25 PM

Can you create a patch for the v3 code? I will probably be removing a lot of the overloads there and replace some parameters with optional parameters...

Jan 10, 2011 at 4:04 PM

OK, I'll get MVC3 set up at home and sort something out.