Tidbits on software development, technology, and other geeky stuff.

Share Razor Partial View between WebForms and MVC

ASP.NET MVC is a breath of fresh air for anyone with a background in ASP.NET WebForms. It’s cleaner, supports the Razor view engine, is much(!) easier to test, doesn’t have the nasty viewstate baggage and generally just feels better. It’s pretty great that you can use it in older webapps as well by Integrating ASP.NET MVC into an existing ASP.NET Webforms application. That’s pretty cool but when you do this, you’ll inevitably have view content that you need to share between WebForms and MVC pages. A perfect example of this is a navigation bar or page footer in MasterPages / MVC Layouts. It’s tempting to assume these two ASP.NET paradigms don’t play together and to just have a Razor version and a separate WebForms (ASPX) version that are synced up manually.

However, they can play together and you can share view content between them if you use a Razor partial view and some bridge code to shim out a wrapper around the WebForms/ASPX HttpContext. The technique was posted on Stack Overflow here and I have been using it successfully in some projects. I consider it a pretty big deal that you can do this as it makes migrating to ASP.NET MVC much more feasible in a legacy web application. I’ve gisted an example to show how’s it’s done:

00_Partial.cshtml

@model PartialViewModel​
<h1>Hello World</h1><p>I was rendered from a <strong>@Model.Source</strong>!</p>

01_RazorPartialBridge.cs

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Web;using System.Web.Mvc;using System.Web.Routing;​
​
//Reference: http://stackoverflow.com/a/1074061/626911public class RazorPartialBridge{public class WebFormShimController : Controller { }public static void RenderPartial(string partialName, object model){//get a wrapper for the legacy WebForm contextvar httpCtx = new HttpContextWrapper(System.Web.HttpContext.Current);​
​
        //create a mock route that points to the empty controllervar rt = new RouteData();​
        rt.Values.Add("controller", "WebFormShimController");​
​
        //create a controller context for the route and http contextvar ctx = new ControllerContext(new RequestContext(httpCtx, rt), new WebFormShimController());​
​
        //find the partial view using the viewenginevar view = ViewEngines.Engines.FindPartialView(ctx, partialName).View;​
​
        //create a view context and assign the modelvar vctx = new ViewContext(ctx, view,new ViewDataDictionary { Model = model },new TempDataDictionary(), httpCtx.Response.Output);​
​
        //render the partial view​
        view.Render(vctx, System.Web.HttpContext.Current.Response.Output);}}

02_Demo.aspx

<%@ Page Title="Demo" Language="C#"​
    AutoEventWireup="true" Inherits="Demo" Codebehind="Demo.aspx.cs" %><html><head></head><body><% RazorPartialBridge.RenderPartial("_Partial", new PartialViewModel() { Source = "ASPX Page" }) %></body></html>

03_Demo.cshtml

<html><head></head><body>​
     @{ Html.RenderPartial("_Partial", new PartialViewModel() { Source = "Razor View" }); }</body></html>

Discuss on Twitter