Rendering Sublayouts in Sitecore MVC

mvc Sitecore sublayout view rendering

There’s no denying that Web Forms is dying and MVC has taken its place… at least in my opinion. The Sitecore guys know it, and it’s evident that there has been a major shift to the latter. If you’re starting out as a Sitecore developer chances are you’re gonna start with MVC. But what if you’ve already started with Web Forms and want to do the transition without giving up your precious sublayouts just yet?

Once upon a time, I was tasked to plan for a Sitecore MVC transition in one of our projects, and to be honest it’s a lot more work than I had imagined since our default ASPX layout file has a bunch of statically added Sitecore sublayouts in it which unfortunately will never run on a View file. Then it dawned to me that if I could just get the literal rendering of a sublayout all my problems would be solved. I started googling for any acceptable solution out there so I could save some precious time and, luckily, after a few clicks, I found this post by Joe Bissol, in which he created an HtmlString extension that renders the usercontrol on a page and returns the literal value of the executed page.

 

The Solution

using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.UI;
using Sitecore.Collections;
using Sitecore.Mvc.Presentation;
using Sitecore.Web;
using Sitecore.Web.UI.WebControls;
using Sitecore.Mvc;

public static class SublayoutMvcAdapterExtensions
{
    public static IHtmlString RenderSitecoreSublayout(this HtmlHelper html, string path)
    {
        var rendering = html.Sitecore().CurrentRendering;
        return html.RenderSitecoreSubLayout(path, rendering.DataSource, rendering.Parameters);
    }

    public static IHtmlString RenderSitecoreSubLayout(this HtmlHelper html,
        string path, string datasource = null, RenderingParameters parameters = null)
    {
        var page = new Page();

        AddSublayoutToPage(page, path, datasource, parameters);
        var result = ExecutePage(page);

        return html.Raw(result);
    }

    private static void AddSublayoutToPage(Page page, string path, string datasource,
        IEnumerable<KeyValuePair<string, string>> parameters)
    {
        var dict = new SafeDictionary();
        if (parameters != null)
            parameters.ToList().ForEach(i => dict.Add(i.Key, i.Value));
        var sublayout = new Sublayout
        {
            DataSource = datasource,
            Path = path,
            Parameters = WebUtil.BuildQueryString(dict, true, true)
        };
        var userControl = page.LoadControl(path);
        sublayout.Controls.Add(userControl);
        page.Controls.Add(sublayout);
    }

    private static string ExecutePage(IHttpHandler page)
    {
        var text = new StringWriter();
        HttpContext.Current.Server.Execute(page, text, true);
        return text.ToString();
    }
}

I opted for this solution since I wanted to start creating new pages using Sitecore MVC while keeping the current structure untouched, and have someone else convert those usercontrols into views (queue evil laugh).

 

Declaring the sublayouts in a View file

@Html.RenderSitecoreSubLayout("~/Layouts/Sublayouts/Common/Header/Header.ascx")
@Html.Sitecore().Placeholder("content")
@Html.RenderSitecoreSubLayout("~/Layouts/Sublayouts/Common/Footer/Footer.ascx")

Rendering a sublayout programmatically

The solution above works perfectly if a sublayout does not contain any postback logic, or if it does not have another sublayout in it. In the latter case you’re gonna have to generate the inner sublayout programmatically and add it to an ASP.Net placeholder so it gets rendered as well.

public Sublayout GenerateSublayout(Item datasource, string id, string controlPath, Action action = null)
{
    var sublayout = new Sublayout();
    sublayout.ID = id;
    sublayout.Path = controlPath;
    sublayout.DataSource = datasource.Paths.Path;

    if (action != null)
        action(sublayout);

    if (Sitecore.Context.Item.Visualization.GetLayout(Sitecore.Context.Device).FilePath.EndsWith(".cshtml"))
        sublayout.Controls.Add(LoadControl(sublayout.Path));

    return sublayout;
}

Here’s an example of how you can generate a sublayout using the function above, and extend its properties:

var copyrightSublayout =
    GenerateSublayout(Sitecore.Context.Item, "scCopyright", 
        "~/Layouts/Sublayouts/Common/Footer/Copyright.ascx",
        (n) =>
        {
            n.Cacheable = true;
            n.VaryByData = true;
        });

CopyrightPlaceholder.Controls.Add(copyrightSublayout);

Happy programming!

No Thoughts to Rendering Sublayouts in Sitecore MVC

Comments are closed.