When we left off last time, I showed you how to write ASP.NET MVC code in C#, then consume and expose that functionality within a VB.NET WebForms project. Why? Because I’m maintaining a project with a huge investment in VB.NET WebForms, and a wholesale migration isn’t feasible. This approach allows for a gradual migration from VB.NET WebForms to C# ASP.NET (with Razor!) So far, all I’ve shown you how to do is render Razor views. But what about all those existing VB.NET master pages? Today I’ll show you how you can use these master pages as “layouts” for your Razor views.

Today’s Goals

Our app doesn’t really do all that much right now. Sure, we’ve managed to get C#-controllers with Razor views to render within a VB.NET application, but that’s not really all that useful. As it stands, we’d have to duplicate our master pages and their functionality as Razor layouts in order to achieve a consistent look-and-feel. That would mean double-maintenance for any changes to the master pages, which is going to be tedious and easy to overlook. That’s not acceptable. Our goals for this post are:

To be able to set the page title from our Razor views (as you’ll see, this is no small feat).

To write our controllers and actions as if there’s nothing fishy going on; they should look like normal, everyday action methods that return normal view results.

While you can easily render WebForms MVC views with WebForms master pages, Razor views require some trickery. This is because of the drastically different ways the two view technologies work. WebForms builds up a control tree (in the form of an object graph) in one pass, then actually generates HTML in a second pass. Razor, on the other hand, pretty much writes directly and immediately to the response. There is a well-documented solution though, as Matt Hawley and Scott Hansleman demonstrated back in 2011.

Instead of directly rendering the Razor view, you first render a WebForms view (shown above), which then renders the real Razor view as a partial. This delays the Razor template from executing until the WebForms control tree has been built up. The downside to the approach demonstrated by Matt and Scott is that you cannot return a normal ViewResult anymore. Instead, you must return a special ActionResult, like so:

That may not seem like a big deal at first, but it ties your day-to-day MVC code to this hackery, meaning you’ll have even more work ahead of you once you are ready to cut ties to WebForms. An ideal solution would enable you to write MVC code as if you weren’t using this WebForms wrapper, making it far easier to convert to real Razor layouts at some point down the road.

Another problem with this approach is that it does not handle page titles. In Razor, the common approach is for views to store the title in ViewBag, and for the view’s layout to retrieve the title from there. This approach does not work for WebForms master pages since they do not have access to the ViewBag, nor can the wrapper easily pass that information over since the page’s title has already been rendered by the time the Razor view is actually executed.

Let’s look at how to overcome these limitations.

Clean Action Methods – The RazorBridgeActionInvoker Solution

One of the extensibility points exposed by the ASP.NET MVC infrastructure is the IActionInvoker interface. The implementer of this interface is responsible for, as you might have guessed, actually invoking an action method on a controller and executing its result. For whatever reason, the MVC team decided that the best way to expose this hook was through overriding a method in your Controller class instead of the more IoC-friendly approach they used elsewhere.

Still, if you’re using layer supertypes (which you should be for your controllers), it’s quite easy to plug in your own action invoker, and that’s exactly what we’re going to do. We’ll take the same approach that Matt and Scott took with wrapping the Razor views in a WebForms view, but we’ll perform the wrap in the action invoker instead of within the action method itself. This pushes the bridge code out of our action methods and frees us from thinking about the hack in our day-to-day coding. It will also make it trivially simple to drop the hack when we’re ready. All we’ll have to do is take out the custom action invoker. Our other MVC code will remain unchanged.

Next, we’ll need the actual action invoker, RazorBridgeActionInvoker. When an action method returns a ViewResult, the invoker needs to wrap it with the special “bridge” action result that links it to the WebForms master page. We can derive from the default invoker to layer on our new behavior:

Notice how the WebForms master here is referencing another WebForms master that doesn’t actually exist? That master actually exists in our VB.NET application and will be resolved at runtime, so ignore the warning that Visual Studio and/or Resharper will most likely give you.

Critical sidenote: In my last post, I modified the MVC project file to include a slew of files as embedded resources so that the embedded virtual path provider could find them, but I missed one very important file type: WebForms master pages! Edit your MVC project file, and change your BeforeBuild target to the following:

Load up the app and navigate to the Hello/World action, and you should see your view nested within the VB.NET master page:

We’re almost finished, now we just need to provide some way to change the title.

It Gets Hacky – Supporting Razor-Style Page Titles

It wouldn’t be a hack without a little JavaScript. To be fair, this is my least-favorite part of the solution, and I would love to find something else. Unfortunately, everything else I tried failed. The fact is that the Razor view is executed too late in the WebForms rendering process to actually change the page title directly. So what can we do? Why, change the title with JavaScript, of course! We can use the HttpContext’s Items collection to pass a title from Razor to WebForms, where with just a little bit of JavaScript trickery, our RazorBridgeView can now set the page title:

You will need to register the custom base view in both your MVC project’s ~/Views/Web.config (for Visual Studio to provide Intellisense) as well as your WebForms project’s ~/Views/Web.config (which will actually be used to select the base page at runtime):

With the custom base ViewPage in place, your views can remain free of references to the HttpContext.

I admit this is hacky, but it has the advantage of actually working, whereas every other solution I investigated failed. If anyone knows of something better, please let me know!

Wait, There’s More!

Well, there is, but it will have to wait until next post. All this MVC functionality is great, but how do you start tying in to it from your existing WebForms? How do you generate strongly-typed links? And how can you start migrating parts of large, complex pages to MVC without rewriting the entire page at once? We’ll explore solutions to those challenges in the next post. In the mean time, feel free to hop over to Github to check out my BlackMagicMvc sample application.

About Matt Honeycutt...

Matt Honeycutt is a software architect specializing in ASP.NET web applications, particularly ASP.NET MVC. He has over a decade of experience in building (and testing!) web applications.
He’s an avid practitioner of Test-Driven Development, creating both the SpecsFor and SpecsFor.Mvc frameworks.

He's also an author for Pluralsight,
where he publishes courses on everything from web applications to testing!