If you have a legacy Sitecore solution that has about a zillion web user controls (or sublayouts as they are named in the wonderful world of Sitecore) you obviously need to perform a major rewrite into MVC.
The powers that be might even agree with you that this is necessary, but alas it can’t be done until a few new features have been developed. Which might indicate that it will take a long time until the Big Rewrite occurs.
If you’re unwilling to add even more webforms code to the solution, you could try developing new components using MVC, if there was an easy way to call these components from a Sitecore layout that uses webforms. There are probably several ways to accomplish this, but here’s one way.
Create a sublayout that uses code to call another Sitecore item which in turn uses an MVC layout and MVC renderings.
- Create a new Device definition item in Sitecore. I call my device “Ajax” and trigger it with a simple querystring.
- Create a new MVC layout called something like MvcAjax.cshtml. This layout should ideally only contain a div with one placeholder named something like “main-mvc”.
@Html.Sitecore().Placeholder("main-mvc")
- Add if statements to avoid rendering Html and Body tags except when using Experience Editor and not the Ajax device. This is useful for testing your components without embedding them in webforms.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@using Sitecore.Mvc | |
@using Sitecore.Mvc.Analytics.Extensions | |
@using Sitecore.Mvc.Presentation | |
@model RenderingModel | |
@{ | |
Layout = null; | |
} | |
@if (Sitecore.Context.PageMode.IsExperienceEditor && Sitecore.Context.Device.Name != "Ajax") | |
{ | |
<!DOCTYPE html> | |
@:<html> | |
<head> | |
<title></title> | |
@Html.Sitecore().VisitorIdentification() | |
</head> | |
@:<body> | |
} | |
<div> | |
@Html.Sitecore().Placeholder("main-mvc") | |
</div> | |
@if (Sitecore.Context.PageMode.IsExperienceEditor && Sitecore.Context.Device.Name != "Ajax") | |
{ | |
@:</body> | |
@:</html> | |
} |
- If you need a visitor context in your MVC components when they are running “standalone” you also need to include the Visitor Identification snippet in the head of the layout.
@Html.Sitecore().VisitorIdentification()
- You might also include styles and scripts in the layout if needed.
- Create a matching Layout definition item in Sitecore and map it to your new view file. Now you have an MVC layout to host your MVC controllers.
- Create new rendering components using MVC at your heart’s content. In my case I have a shopping cart component and a checkout component. The controller action will populate and load the cart content, but I show only the cart controller rendering definition here since the cart itself is outside the scope of this post.
- In Sitecore, you need to create items that display your MVC components on the MVC layout. In my case I have a “Cart Page” item and a “Checkout Page” item. I show only the cart page here.
- The layout details for the cart page uses the MVC layout and the cart MVC rendering for both the default device and the Ajax device.
- The MVC part is now done. Time to call MVC from webforms! The trick is to create sublayouts which in code render MVC pages. In my example I create one sublayout for displaying the cart and another sublayout for displaying the checkout.
- My sublayouts use jQuery to render the MVC items statically, but this could of course be solved more elegantly and with more modern techniques. Note the ajax=true querystring to control the device used for the request.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<%@ Control Language="c#" AutoEventWireup="true" TargetSchema="http://schemas.microsoft.com/intellisense/ie5" %> | |
<asp:Panel runat="server" ID="pnlMvcShoppingCart" ClientIDMode="Static"> | |
<div class="loader loader-bar" style="margin: 200px 0"></div> | |
</asp:Panel> | |
<script src="/ui/trede/js/libs/jquery-2.2.4.min.js"></script> | |
<script type="text/javascript"> | |
$(document).ready(function() { | |
(function () { | |
$("#pnlMvcShoppingCart").load("/<%=Sitecore.Context.Language.Name%>/Commerce/Pages/Cart%20Page?ajax=true", function (response, status, xhr) { | |
if (status == "error") { | |
var msg = "Sorry but there was an error: "; | |
$("#pnlMvcCheckout").html(msg + xhr.status + " " + xhr.statusText); | |
} | |
}); | |
})(); | |
}); | |
</script> |
That’s it, really. The MVC rendering will use the same visitor context as already loaded in the webforms layout. It can reuse the style and script references from the webforms layout as well.
Here’s how the final page looks in the browser with the webforms parts blurred.
The most satisfying part of a solution like this is to be able to create new functionality using new technology without being forced to refactor all the legacy code.
Thanks for reading and remember, if you don’t own the experience, the experience might own you!