sebnilsson.com | Liquid Development Is What I Do
Seb Nilsson

Render .ascx-Files in ASP.NET MVC Using Only RazorViewEngine

ASP.NET Band-Aid

If you're stuck in an environment where you're migrating from ASP.NET MVC to ASP.NET WebForms it's good to know that you can actually render your existing WebForms-Controls in you MVC-views. This might sound like a crazy thing to do (and it is in the long run!) but it might be useful if you're stuck between sprints and have perfectly working WebForms-Controls (.ascx-files) that you don't have time to migrate right now. All you have to do is use the HtmlHelper's helper-method .RenderPartial(string partialViewName) and pass it the path to the WebForms-Control.

// Write the content of a control inside a view:
Html.RenderPartial("~/Controls/ControlVirtualPath.ascx");

// Or to get the content of a control as a MvcHtmlString, for further manipulation:
Html.Partial("~/Controls/CustomControl.ascx")

It's important that your controls inherits from System.Web.Mvc.ViewUserControl and NOT the old System.Web.UI.UserControl.

One performance-tip that is often mentioned around ASP.NET MVC is to deactivate the WebForms-View Engine for MVC Razor-views (which actually turns out to maybe not make such a big difference after all). This will not prevent .aspx, .ascx and other WebForms-files from working.

But you still want your .ascx-files to work inline in your MVC Razor-views. This can be achieved by implementing your own class that inherits RazorViewEngine, which only uses the WebFormViewEngine when actually needed.

Global.asax (or other config-class)

// Remove WebFormViewEngine (and RazorViewEngine)
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new CustomRazorViewEngine());

CustomRazorViewEngine : System.Web.Mvc.RazorViewEngine

private static readonly WebFormViewEngine WebFormsEngine = new WebFormViewEngine();

public override ViewEngineResult FindPartialView(
    ControllerContext context, string name, bool useCache)
{
    if (name.EndsWith(".ascx"))
    {
        return WebFormsEngine.FindPartialView(context, name, useCache);
    }

    return base.FindPartialView(context, name, useCache);
}

If you need the actual class of the control to do some further analysis/manipulation, you can do the following anywhere in your code:

var viewPage = new ViewPage();
var control = viewPage.LoadControl("~/Controls/ControlVirtualPath.ascx") as ControlType;

ASP.NET MVC 4 Razor v2 - New Features

Reference

With the imminent release of ASP.NET MVC 4 my previous post ASP.NET MVC 3 Razor - The Ultimate Quick Reference needed a follow-up on what's new in the view-engine Razor v2, that comes with MVC 4.

App-relative URLs automatically resolved

In Razor v1 your views would be littered with code like this:

<script src="@Url.Content("~/content/scripts.js")"></script>

But in Razor v2 you can shorten it with a tilde in front of the URL, which was used a lot in WebForms:

<script src="~/content/scripts.js"></script>

This will work for any HTML-attribute that starts with the title-sign.

Conditional attributes hides null-valued attributes

Razor v1 treated null-valued attributes as empty string, writing out attributes empty values. Razor v2 will instead skip even writing out the attribute that has a null value:

@{
    string itemId = string.Empty;
    string itemClass = "class-of-item";
    string itemValue = null;
}
<div id="@itemId" class="@itemClass @itemValue" rel="@itemValue"></div>

Will result in this HTML:

<div id="" class="class-of-item"></div>

Note that string.Empty still makes Razor v2 to write out the attribute. Any data-attribute will ignore this rule and always write out the attribute.

Support for unclosed HTML-tags

The HTML5-specs clearly states that unclosed HTML-tags are supported, but Razor v1 didn't have an advanced enough parser to support this. Razor v2 now supports this with the elements listed in W3C's spec.

What else?

There has been some major improvements on how the syntax-trees are structured and other deep functionality in the language. The focus was internal clean-up and future-proofing.

Making MVC 3 Razor IntelliSense work after installing MVC 4 Beta

ASP.NET

After installing the MVC 4 Beta the IntelliSense breaks for Razor-views in MVC 3-applications in Visual Studio 2010. This is stated in the release-notes, but nobody usually reads those.

This time the solution to the problem are actually listed in those release-notes. You need to the explicitly state the version-numbers of the references in your web.config.

Add a new appSettings-entry for explicitly stating the version of WebPages to use:

<appSettings>
    <add key="webpages:Version" value="1.0.0.0"/>
    <!-- ... -->
</appSettings>

Then you have to edit your .csproj-file, where you need to find your references to System.Web.WebPages and System.Web.Helpers and makes sure they have the explicit version-numbers like this:

<Reference Include="System.Web.WebPages, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"/> 
<Reference Include="System.Web.Helpers, Version=1.0.0.0,
Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL" />

Hopefully this will be resolved in the final version of MVC 4 or maybe the case is that the references to versions in Razor v1 were just too loose in MVC 3-projects.

ASP.NET MVC 3 Razor - The Ultimate Quick Reference

Reference

With the transition to the Razor view-engine in ASP.NET MVC 3 a need occurred for a quick-reference.

Sections - Replacement for 'asp:ContentPlaceHolder' and 'asp:Content'

Where you the old WebForms view-engine used to use the controls and to dynamically switch out parts of the content on a page, the Razor view-engine uses sections.

_Layout.cshtml (Equivalent to a .master-page):

<div>Template content</div>
@RenderBody()
<div>More template content</div>
@RenderSection("OtherContent", required: false)

Page.cshtml:

@section OtherContent {
    <span>Other content goes here</span>
}
<div>Main page-content</div>

In above code the RenderSection-method in the _Layout.cshtml renders the content within the matching @section in the Page.cshtml. The required-parameter indicates if the section is required and should throw an exception if the section doesn't exist. The RenderBody-method renders the rest of the content in the Page.cshtml, which is not inside any section.

Declaring the ViewModel

In the old WebForms view-engine your declaration of the view-model would look like something like this:

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

While in the Razor view-engine declaring your view-model is as easy as this:

@model MyViewModel

Inline C#-code in the HTML

If you wanted to loop over a collection in your view-model in WebForms view-engine your code would look something like this:

<% foreach(var item in Model.Items) { %>
    <% if(item.IsValid()) { %>
        <div class="<%: item.ClassName %>">&;lt;%: item.Title %></div>
    <% } %>
<% } %>

The percent-tags make it a bit hard to quickly get an overview of what is HTML and what is code. This is one of the largest upsides to the Razor view-engine, where the same code would be written much more readable like this:

@foreach(var item in Model.Items) {
    if(item.IsValid())
    {
        <div class="@item.ClassName">@item.Title</div>
    }
}

If you make a proper installation of MVC 3 you will get syntax-highlighting in Visual Studio for separating C#-code and HTML-tags.

Advanced inline C#-code

Razor is extremely intelligent when it comes to determining when code starts and ends behind the @-signs, which is very well visualized by the IntelliSense in Visual Studio.

<div>This is @Model.Name's page</div>

Since the apostrophe doesn't follow any pattern that could make it correct C#-code, Razor knows to cut the code from there.

If you want to be extra clear in your code, against other developers, but also against Razor, as to where the code starts and ends, you can use parentheses like this:

<div>This is @(Model.Name)'s page and has the status "@(Model.Status)"!</div>

Inline C#-code for logic

You can include code on the page that is not necessarily written out on the page directly, but handles logic and page-specific functionality. For this you use the @-sign, followed by curly braces.

<div>Other content</div>
@{
    string capitalizedTitle = Model.Title.ToUpper();
}
<h1>Page about @capitalizedTitle</h1>

This is used in the templates that comes with MVC 3 to set which Layout-file to use. But you can use this on your own for setting the title-text in the title-tag.

Page.cshtml:

@{  
    ViewBag.Title = "Index titel - " + Model.SubTitle;
}

_Layout.cshtml:

<head>
    <title>@ViewBag.Title</title>
    <!-- ... -->

Helpers - Reusing code specific to view-template

With the new @helper-syntax in Razor you can easily write re-usable methods that deals with output-functionality within view-templates.

@helper ShowInfo(string name, bool hasContent) {
    if(hasContent) {
        <div>This is info about @name.ToUpper()</div>
    }
}

Namespaces

Using a namespace is very easy and reminds us a lot of the old way to do it:

@namespace System.ComponentModel.DataAnnotations

Server-side comments

To do server-side comments with Razor you can click the "Comment" toolbar button or press Ctrl+K, Ctrl+C to apply a server-side comment. The commented code is place between a @* and a *@ and looks like this:

@" This is the text of the model. MUST be HTML-encoded. "@
<div class="special-elemet">@Model.Text</div>

Global settings for view-templates

If you place a file named _ViewStart.cshtml in the root of the ~/Views-folder, the Razor-code in this code will be applied as default to all view-templates in the sub-folders. In the templates that comes with MVC 3 this is used to set the default Layout-file to be used for the view-templates.

@{
    Layout = "~/Views/Shared/_Layout.cshtml";
}

Not HTML-encoding content

In the old WebForms view-engine you could use a colon-character instead of an equals-sign to HTML-encode content like this:

<%= ContentToBeOutputWithoutEncoding %>  
<%: ContentToBeEncoded %>

The Razor view-engine automatically HTML-encodes whatever strings you output without explicitly using Html.Raw-method like this:

@Html.Raw(ContentToBeOutputWithoutEncoding)

The <text>-tag for Razor

The way Razor analyzes the code to find the start and end only makes it it possible to have one element (with multiple children of course) inside an expression. To resolve this Razor introduces a <text>-tag, where you can nest multiple lines of code:

<text>
    <div>One nested item</div>
    <span>Another nested item</span>
</text>