1
votes

I am currently implementing breadcrumb in a MVC3 Razor application, using Maartenba's MVC Sitemap Provider. The problem occurs when I try to create dynamic nodes, for the blog posts. What I want to achieve is to display Home>Blog for the blog page and Home>Blog>BlogPostTitle for the post page, where BlogPostTitle is the title of the currently displayed post. What I actually get is: Home>Blog both for the blog page and for any post page.

To test the dynamic nodes generation, I also included on the _Layout a call to @Html.MvcSiteMap().Menu(false, true, true). The displayed menu confirms that the nodes are correctly generated:

  • Home
  • About
  • Blog
    • Article #1 title
    • Article #2 title
  • Contact

Still, the @Html.MvcSiteMap().SiteMapPath() displays Home>Blog both for the blog page and for the articles.

Here is the code:

Sitemap:

<mvcSiteMapNode title="Home" controller="Home" action="Index">
    <mvcSiteMapNode title="About" controller="Home" action="About"/>
    <mvcSiteMapNode title="Blog" controller="BlogPost" action="Index" key="News" >
        <mvcSiteMapNode title="" controller="BlogPost" action="Details" dynamicNodeProvider ="MyApp.Helpers.BlogPostDynamicNodeProvider, myApp" />
    </mvcSiteMapNode>
    <mvcSiteMapNode title="Contact" controller="Home" action="Contact"/>
</mvcSiteMapNode>

Dynamic node provider:

public class BlogPostDynamicNodeProvider : DynamicNodeProviderBase
{
    public BlogPostRepository _repository = new BlogPostRepository();
    public List<BlogPost> articles = new List<BlogPost>();

    public override IEnumerable<DynamicNode> GetDynamicNodeCollection()
    {
        articles = _repository.FindAllBlogPosts();
        var returnValue = new List<DynamicNode>();

        foreach (var article in articles)
        {
            DynamicNode node = new DynamicNode();
            node.ParentKey = "News";
            node.Title = article.PostTitle;
            node.RouteValues.Add("id", article.Post_ID);
            returnValue.Add(node);
        }
        return returnValue;
    }

    public override CacheDescription GetCacheDescription()
    {
        return new CacheDescription("BlogPostDynamicNodeProvider")
        {
            SlidingExpiration = TimeSpan.FromMinutes(1)
        };
    }
}

BlogPostCOntroller

    public ViewResult Index(long? BlogPost_ID)
    {

        var BlogPostList = new PagedData<BlogPost>();
        BlogPostList.Data = repository.FindAllBlogPostsPaged(1, 3, 1);

        BlogPostList.NumberOfPages = Convert.ToInt32(Math.Ceiling((double)repository.NumberOfPosts(1) / 3));
        BlogPostList.CurrentPage = 1;

       BlogPost pBlogPost = new BlogPost();
            pBlogPost.PostDate = DateTime.Now;

            return View(new BlogPostPagedViewModel(pBlogPost, repository.FindAllBlogPosts()
                new SelectList(ctgRepository.FindAllBlogCategories(), "Category_ID", "Category_Name")));
    }

    [SiteMapPreserveRouteData]
    public ActionResult Details(long id)
    {
        BlogPost post = repository.FindABlogPostByID(id);
        return PartialView("_Details", post);
    }

_Layout

<div id="pagecontent">
    @Html.MvcSiteMap().Menu(false, true, true)   <!-- for test purpose only -->
    @Html.MvcSiteMap().SiteMapPath() 
    @Html.Partial("_PagedList", Model.BlogPostList)
</div><!-- page blogcontent -->

Blog Index view

<div id="blogcontent">
    @Html.Partial("_PagedList", Model.BlogPostList)
</div><!-- end blogcontent -->

Blog _PagedList partial view

@using (Ajax.BeginForm(new AjaxOptions { UpdateTargetId = "blogcontent", InsertionMode = InsertionMode.Replace }))
{
    foreach (var item in Model.Data)
    {
        <h2 class="posttitle">
            @Ajax.ActionLink(item.PostTitle, "Details", "BlogPost", new { id = item.Post_ID }, new AjaxOptions { UpdateTargetId = "blogcontent" }, null)            
        </h2>
    }
}

Details view

@model MyApp.Models.BlogPost
@using (Ajax.BeginForm(new AjaxOptions { UpdateTargetId = "blogcontent", InsertionMode = InsertionMode.Replace }))
{
    <div class="blogpost">
    ...
    </div>
}

_ Edit _ I discovered that, if I remove @Html.MvcSiteMap().SiteMapPath() from _Layout and place it instead in the blog Index view and in the Details view, the breadcrumb displays the current location correctly.

But that means I need to place it on every page from the project, which is in contradiction with the role of the _Layout shared view!

Where is the mistake in my code?

3

3 Answers

0
votes

Try setting Controller and Action when creating the DynamicNode:

DynamicNode node = new DynamicNode();
node.Controller = "ControllerName";
node.Action = "ActionName";
node.ParentKey = "News";
node.Title = article.PostTitle;
node.RouteValues.Add("id", article.Post_ID);
returnValue.Add(node);

This information is used to choose the correct route.

0
votes

In you Index action you have an id parameter, if you remove that and apply @Xharze suggestion, does that make any difference. Do you have any routes defined in the web.config specific to blogs/posts ?

0
votes

Remove the SiteMapPreserveRouteDataAttribute from your controller action.

The SiteMapPreserveRouteDataAttribute (which has now been deprecated) is not designed to work in conjunction with dynamic nodes. And there is no need, since you are adding the RouteValues explicitly in your dynamic node provider anyway.