Rendering Sublayouts in Sitecore MVC

I have recently been 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!

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s