Scott Hanselman

Mixing Razor Views and WebForms Master Pages with ASP.NET MVC 3

January 25, '11 Comments [16] Posted in ASP.NET | ASP.NET MVC | VS2010
Sponsored By

I've spent the last few weeks travelling around presenting WebMatrix, Razor and ASP.NET MVC 3 to folks. Many people have existing WebForms apps or MVC apps with WebForms Views. The question has come up a number of times, "Can I mix Razor Views and WebForms Views in a single ASP.NET MVC application?"

The answer is, "No, Yes, and Maybe, But It's Not Supported."

Most commonly the scenario is that someone has an existing WebForms (ASPX) Master Page that works nicely, and they now want to include a few Razor pages in their application but don't want to maintain two effectively identical Master Pages (one for ASPX, one for Razor). They want to share their WebForms Master with both WebForms and Razor Views.

First, the No.

You can't directly have a Razor Layout with WebForms Views, or a WebForms Master with Razor Views. While the concepts are similar, layouts are not masters and the concepts aren't exactly interchangeable.

Second, the Yes.

However, Html.RenderPartial or Html.RenderAction is an opportunity to move between view engines. As Eilon Lipton says, the idea of a partial view or controller action is a first-class concept in ASP.NET MVC, but master pages are implementation details of the specific view engines.

You can put shared content in Partials then call them from wherever. For example, from Eilon's suggestion, let's say you had these three WebForms partial views:

  • Header.ascx: Contains top navigation, menu, etc.
  • Footer.ascx: Contains copyright notice, privacy link, etc.
  • SearchBox.ascx: Contains search box implementation.

Then you have a basic WebForms master that references the above three user controls.

  • WebFormLayout.master: Lays out with the controls from above.
  • Page1.aspx: Uses WebFormLayout.master.
  • Page2.aspx: Uses WebFormLayout.master.
  • Page3.aspx: Uses WebFormLayout.master.

Then let's say you have a Razor Layout (Razor Layouts are like WebForms Masters) that reference to the same three WebForm user controls.

  • _RazorLayout.cshtml: Lays out with the controls from above.
  • Page4.cshtml: Uses _RazorLayout.cshtml.
  • Page5.cshtml: Uses _RazorLayout.cshtml.
  • Page6.cshtml: Uses _RazorLayout.cshtml.

This is the cleanest, easiest, and most supported way for doing things. You will have to have some minimal duplication between your Razor Layout and your WebForms Master, but most of the shared content is in the .ascx partials. You can mix this up as you like, but you get the idea.

Maybe, But It's not Supported

Matt Hawley from CodePlex took a look at this problem and blogged about his own solution for CodePlex. While it might feel like this should be a simple task, it's tricky because WebForms builds up a control tree and then renders it, while Razor is an all new system that's closer to a single pass template. Matt wants to directly share a WebForms Master page.

His solution is

  1. There's a web forms master page
  2. There's a web forms view page called "RazorView" in the \Shared folder
  3. The aspx view renders a partial in the content placeholder that is the actual razor view
  4. There are some new added RazorView extension methods for the controller that return a ViewResult.

Seems complex, but it's actually pretty straightforward. For example to get output like this browser screenshot using Matt's technique...

image

...we start with our WebForms View Site.Master:

<%@ Master Language="C#" Inherits="System.Web.Mvc.ViewMasterPage" %>
<!DOCTYPE html>
<html>
<head runat="server">
<title><asp:ContentPlaceHolder ID="TitleContent" runat="server" /></title>
</head>
<body>
<div>
<h1>I'm a WebForms Site.Master Page View. Seriously.</h1>
<asp:ContentPlaceHolder ID="MainContent" runat="server" />
</div>
</body>
</html>

This is a normal master page. Here's our also normal Index.cshtml (Razor).

<h3>This is the index page. I'm Razor. We've clearly broken some kind of law to be at this point, right?</h3>

This page is basically nothing, and doesn't have a layout or anything specified. 

However, Matt has some extension methods he's added to the Controller class. Instead of returning View() from his controller action, he returns RazorView() like this.

public class HomeController : Controller
{
// GET: /Home/
public ActionResult Index()
{
return this.RazorView();
}
}

Just a reminder as it begs repeating, RazorView is an extension method. Here's the whole ControllerExtensions class from Matt.

public static class ControllerExtensions
{
public static ViewResult RazorView(this Controller controller)
{
return RazorView(controller, null, null);
}

public static ViewResult RazorView(this Controller controller, object model)
{
return RazorView(controller, null, model);
}

public static ViewResult RazorView(this Controller controller, string viewName)
{
return RazorView(controller, viewName, null);
}

public static ViewResult RazorView(this Controller controller, string viewName, object model)
{
if (model != null)
controller.ViewData.Model = model;

controller.ViewBag._ViewName = GetViewName(controller, viewName);

return new ViewResult
{
ViewName = "RazorView",
ViewData = controller.ViewData,
TempData = controller.TempData
};
}

static string GetViewName(Controller controller, string viewName)
{
return !string.IsNullOrEmpty(viewName)
? viewName
: controller.RouteData.GetRequiredString("action");
}
}

Matt's solution is clever in its simplicity. When he calls RazorView() he's returning a ViewResult that's a WebForm View, even though we want a Razor one. This satisfies the whole WebForms master pages system. However, the WebForms view called "RazorView" exists for only one reason. It calls RenderPartial, as that's the right way too switch between view engines.

<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<dynamic>" %>

<asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server">
<h2>I'm a secret WebForms View that lies to everyone and renders Razor stuff. Ssssh! Delete this line!</h2>
<% Html.RenderPartial((string) ViewBag._ViewName); %>
</asp:Content>

Of course, when you use this, remove the <h2>, that's just for illustration. This view is only a shim that enables the switch.

So there you go. Two good options for mixing WebForms and Razor in your ASP.NET MVC application. The first has a little duplication, but is clean. The second is a little trickier and you're on your own, but you can directly share Master Pages between WebForms and Razor Views. You can get the code for the second over at Matt's blog.

Thanks Eilon and Matt!

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook twitter subscribe
About   Newsletter
Sponsored By
Hosting By
Dedicated Windows Server Hosting by ORCS Web
Tuesday, January 25, 2011 9:50:14 PM UTC
Brilliant, Thank you very much Scott
Tuesday, January 25, 2011 9:57:56 PM UTC
GAHHHHH, MY EYES!!!!!!
Wednesday, January 26, 2011 4:09:46 PM UTC
Tnx, I prepare some presentation about MVC 3 for my team so it will helpful.
Thursday, January 27, 2011 5:02:07 PM UTC
Hi Scott,

Any tips or best practices on sharing user controls between MVC and ASPX pages? I have a bunch of ASCX controls with code behinds, and I'm struggling to find a way to use them in Razor views. I don't mind converting them to something else, as long as my old ASPX pages can still use them - I don't want to have to maintain two versions. Googling with Bing hasn't uncovered much so far...
Tuesday, February 15, 2011 6:36:07 AM UTC
what about converting from aspx to razor, not mixing?
Tuesday, February 15, 2011 6:55:49 AM UTC
Geidrius - There are some basic converter tools, like this ASPX to Razor Convertor from Telerik.
Tuesday, February 15, 2011 7:01:32 AM UTC
Well, the biggest problem is, that they are basic :)
What i miss most, is master pages conversion, from one side it's not that straightforward, from other - it should be possible?
Tuesday, February 15, 2011 7:18:25 AM UTC
Automatic conversion? It'll get you 80% of the way there.
Monday, February 21, 2011 6:48:07 PM UTC
Hi Scott,

I'm sure this should be simple, but how can you add a Razor view to an existing MVC 2 app?

Many thanks

Ian
Ian Blackburn
Thursday, March 03, 2011 11:05:29 AM UTC
Great article thanks. Just trying to implement the "Yes" approach and you say:

"... you have a Razor Layout (Razor Layouts are like WebForms Masters) that reference to the same three WebForm user controls."

Can you please provide a code snippet of how you reference these controls in a razor layout? Do we need to switch view engines at some point?

Cheers,
Sandy.
Sandy
Monday, March 07, 2011 12:06:24 AM UTC
Ignore that last post. I stupidly thought you were talking about a magic solution for mixing Code-behind webforms masterpages and razor, not MVC webform master pages! Too good to be true.
Cheers,
Sandy.
Sandy
Tuesday, April 12, 2011 7:22:24 AM UTC
Worked pretty hard on this one for you Scott :)

http://zeusmvc.codeplex.com
Wednesday, May 04, 2011 5:19:35 PM UTC
I have the same question as Sandy, how do you reference the user controls in the Razor Layout? A code sample would be great.
Ryan
Wednesday, May 04, 2011 7:05:15 PM UTC
Can we have other way around? A web form with razor view layout. We are developing an application in MVC3 and the reports pages of the application are developed in webform since report viewer cannot be used in Razor view. But we need to have the same layout for the reports section. Can we do this??
Friday, August 12, 2011 3:28:31 PM UTC
Can you clear up the following Razor/User Control statement from the "Second, the Yes" above?

"Then let's say you have a Razor Layout (Razor Layouts are like WebForms Masters) that reference to the same three WebForm user controls."

How do the Razor layouts reference the same user controls (that may use code-behind and ViewState) that are referenced by the Web Forms Master?

You are talking about rewriting the header, footer , etc. user controls as MVC partial views, right?

Please help clear up this one point.

As always, thanks for a great post.

Dave

PS - Sandy and Ryan asked a similar questions above...

Dave
Wednesday, September 14, 2011 12:49:47 AM UTC
Hi, I have similar questions as Dave, Sandy and Ryan mentioned. How can a razor layout reference the partial view if it is not in Razor syntax?
Please help since I gonna have my web app built with both classic asp.net web forms and mvc 3, this really makes a newbie's head explode..
Thanks and appreciate that..

Ophelia
Ophelia
Comments are closed.

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.