I want to make breadcrump navigate 3 paths to the same page. Site map is at the bottom.
<mvcSiteMapNode title="New" controller="Actions" action="NewActions" area="Promotion">
<mvcSiteMapNode title="Action" controller="Actions" action="ActionTabDetails" area="Promotion"/>
</mvcSiteMapNode>
<mvcSiteMapNode title="Continues" controller="Actions" action="ContinuesActions" area="Promotion">
<mvcSiteMapNode title="Action" controller="Actions" action="ActionTabDetails" area="Promotion" actionStatus="1"/>
</mvcSiteMapNode>
<mvcSiteMapNode title="Finished" controller="Actions" action="FinishedActions" area="Promotion">
<mvcSiteMapNode title="Action" controller="Actions" action="ActionTabDetails" area="Promotion" actionStatus="2"/>
</mvcSiteMapNode>
I tried with atributes "type" and "key" but it didn't help. Everytime when I am opening action ActionDetails, breadcrump looks like
root > Finished > Action
Selection of path is determined in controller, by the status of model. I now I should write own DynamicNodeProvider but I don't known how to pass parameter to the Provider from controler. I couldn't use action annotation, becouse I now condition of model in action body like at the bottom:
public ActionResult ActionTabDetails(Guid actionTabGuid)
{
ActionTab model = actionTabRepo.Get(actionTabGuid, "ActionGroup");
if (model.Status == ActionStatus.New)
{
//Parameter with I want to pass to the DynamicNodeProvider or select current node
}
//another conditions
return View("ActionTab/ActionTabDetails", model);
}
I tried:
SiteMap.CurrentNode = SiteMap.Provider.FindSiteMapNodeFromKey("new");
but in controller is only getter.
I'll be very gratefull for help.
Updated:
I maked action with optional parameter:
public ActionResult ActionTabDetails(Guid actionTabGuid, int actionStatus=0)
but every url like
- /Promotion/Actions/Action?actionTabGuid=822ed729-8edd-4301-970a-867d2b4f9246
- /Promotion/Actions/Action?actionTabGuid=822ed729-8edd-4301-970a-867d2b4f9246&actionStatus=1
- /Promotion/Actions/Action?actionTabGuid=822ed729-8edd-4301-970a-867d2b4f9246&actionStatus=2
direct to the first node without parameter. I've tried with obvers parameters in action, but still effect is the same. I will be gratefull for help.
The key to making it work is to know that every set of route values must be unique within the SiteMap. That is, you need to add another parameter to all but 1 of the routes and either the parameter name or its value must be different on every node.
<mvcSiteMapNode title="New" controller="Actions" action="NewActions" area="Promotion">
<mvcSiteMapNode title="Action" controller="Actions" action="ActionTabDetails" area="Promotion"/>
</mvcSiteMapNode>
<mvcSiteMapNode title="Continues" controller="Actions" action="ContinuesActions" area="Promotion">
<mvcSiteMapNode title="Action" controller="Actions" action="ActionTabDetails" area="Promotion" someParameter="1"/>
</mvcSiteMapNode>
<mvcSiteMapNode title="Finished" controller="Actions" action="FinishedActions" area="Promotion">
<mvcSiteMapNode title="Action" controller="Actions" action="ActionTabDetails" area="Promotion" someParameter="2"/>
</mvcSiteMapNode>
The combination of route values (or the explicit URL set on the url attribute) is how the node is identified as being the current node and the first match always wins. But if you add extra data, then each node will be completely unique.
If you are using the default route, your URLs would then look like this:
- /Promotion/Actions/Action
- /Promotion/Actions/Action?someParameter=1
- /Promotion/Actions/Action?someParameter=2
Note that you can also customize the MVC routes if you prefer by inheriting RouteBase or adding the custom parameter to the route in order to make the URL more user-friendly.
Once you have your URLs set up the way you prefer (that is, the UNIQUE URLs) you can use the canonical tag HTML helper to ensure that only the "main" URL is indexed by the search engines and the others are ignored. You just need to set either the canonicalKey or the canonicalUrl attribute to that of the "main" node.
<mvcSiteMapNode title="New" controller="Actions" action="NewActions" area="Promotion">
<mvcSiteMapNode title="Action" controller="Actions" action="ActionTabDetails" area="Promotion" key="TheMainAction"/>
</mvcSiteMapNode>
<mvcSiteMapNode title="Continues" controller="Actions" action="ContinuesActions" area="Promotion">
<mvcSiteMapNode title="Action" controller="Actions" action="ActionTabDetails" area="Promotion" someParameter="1" canonicalKey="TheMainAction"/>
</mvcSiteMapNode>
<mvcSiteMapNode title="Finished" controller="Actions" action="FinishedActions" area="Promotion">
<mvcSiteMapNode title="Action" controller="Actions" action="ActionTabDetails" area="Promotion" someParameter="2" canonicalKey="TheMainAction"/>
</mvcSiteMapNode>
Then all you have to do add the @Html.MvcSiteMap().CanonicalTag()
HTML helper to the HEAD section in your layout page and the canonical URLs will be created automatically on the alternate pages (but not the "main" one).
See this post for a downloadable example. Also, this post goes into depth about how the node matching functionality works.
MVC Routing
Keep in mind it is the routes that determine how the URL will be constructed. Have a look at your RouteConfig.cs file.
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
As you can see, the default route only uses "id" as the parameter. Anything you add that is not controller, action, or id will become part of the querystring. You can add additional routes, parameters, and constraints as your application requires (more specific routes belong before the default route, and typically you should leave the default route alone). Have a look at MSDN for in depth coverage of routing or google "mvc routing" and you will find lots of great tutorials.
TIP: AttributeRouting makes it easy to provide multiple routes to an action method by decorating it with several Route
attributes.
[Route("new-actions/action-tab-details/{actionTabGuid}")]
[Route("continues-actions/action-tab-details/{actionTabGuid}")]
[Route("finished-actions/action-tab-details/{actionTabGuid}")]
public ActionResult ActionTabDetails(Guid actionTabGuid)
{
ActionTab model = actionTabRepo.Get(actionTabGuid, "ActionGroup");
if (model.Status == ActionStatus.New)
{
//Parameter with I want to pass to the DynamicNodeProvider or select current node
}
//another conditions
return View("ActionTab/ActionTabDetails", model);
}
If you don't want to mess with routing, I would suggest just using the default route and using "id" as your Guid value (since most actions will only have one anyway). You don't need to add "actionStatus" to your action method if it doesn't mean anything to your application and your route doesn't specify it as a required value (the default route does not).
MvcSiteMapProvider Routing
On the other side of this is the node matching for MvcSiteMapProvider. When using custom route values (parameters), you need to configure MvcSiteMapProvider so it understands you are using custom parameters. You do this by either adding every possible combination of them as a separate node (using IDynamicNodeProvider or ISiteMapNodeProvider) or you will need to force every value to match a single node with preservedRouteParameters. Use individual nodes for each value if it is important that all of the pages are indexed in search engines. Use preservedRouteParameters if your pages are primarily for data entry. Usually, when using preservedRouteParameters you also have to use SiteMapTitleAttribute and a visibility provider to adjust the look of the menu and breadcrumb trail. There are demos of both of these techniques for download on this post.