There are a number of view engines available in addition to the ones that come built-in to the MVC framework – and fortunately, there are all much more richly features than the one we have created in this chapter.
Four of the most popular are Spark, NHaml, Brail and NVelocity. Some of these are ports of view engines from other languages or web platforms – NVelocity is a port of the Java Apache Velocity template engine and NHaml is a port of the Ruby on Rails Haml engine. The most popular, Spark, is the most interesting in that it allows programming logic to be expressed in the flow of HTML elements, which can make templates more readable. We have not spent any significant time using these alternative engines, but we know some good programmers who swear by them, If you find that Razor or the ASPX engine isn’t what your project needs, you might find what you are looking for in one of these additional engines.
Working with the Razor Engine
In the previous section, we were able to create a custom view engine by implementing just two interfaces. Admittedly, we ended up with something very simple, and which generated very
ugly views, but we saw how the concept of MVC extensibility continues throughout the request processing pipeline.
The complexity in a view engine comes from the system of view templates which include code fragments, support layouts and which are compiled to optimize performance. We didn’t do any of these things in our simple custom view engine – and we didn’t need to, because the Razor engine takes care of all of that for us. The functionality that almost all MVC
applications require is available in Razor and only a vanishingly small number of projects need to go to the trouble of creating a custom view engine.
We are going to show you different techniques for working with Razor – as a reminder, this is the view engine that was introduced with MVC 3 and which replaces the previous view engine (known as the ASPX or Web Forms engine). You can still use the ASPX view engine and Microsoft has not said that they are deprecating it – but our feeling is that the future of view engines in MVC is clearly Razor. We gave you a primer of the new Razor syntax in Chapter 5.
In this chapter, we are going to show you how to apply that syntax and other features to create and render Razor views and show you how to customize the Razor engine.
Understanding Razor View Rendering
The Razor view engine compiles the views in our applications to improve performance. The views are translated into C# classes and then compiled, which is why we are able to include C#
code fragments so easily. It is instructive to look at the source code that Razor views generate – it helps to put many of the Razor features in context. Listing 6 shows a simple Razor views that takes an array of strings as the view model object
Listing 6. A simple Razor view
@model string[]
@{
ViewBag.Title = "Index";
}
This is a list of fruit names:
@foreach (string name in Model) { <span><b>@name</b></span>
}
The views in an MVC application are not compiled until the application is started, so to see the classes that are created by Razor, we have to start the application and navigate to an action method – any action method will do, since the initial request to an MVC application triggers the view compilation process.
Conveniently, the generated classes are written to the disk as C# code files and then compiled, which means that we can see the C# statements that a view ends up being represented with. The You can find the generated files are in the
c:\Users\yourLoginName\AppData\Local\Temp\Temporary ASP.NET Files folder in on Windows 7, which is the operating system we use for development. The location on other version of windows will vary slightlyor the equivalent folder on your operating system version and installation location. Finding the code file generated for a particular view requires a bit of poking around – there are usually a number of folders with cryptic names and the names of the .cs files don’t correspond to the names of the classes they contain. As an example, we found the generated class for the view in Listing 6 in a file called App_Web_gvaxronl.1.cs in the
root\83e9350c\e84cb4ce folder. We have tidied up the class from our system to make it easier to read – it is shown in Listing 7.
Listing 7. The generated C# class for a Razor view namespace ASP {
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Web;
using System.Web.Helpers;
using System.Web.Security;
using System.Web.UI;
using System.Web.WebPages;
using System.Web.Mvc;
using System.Web.Mvc.Ajax;
using System.Web.Mvc.Html;
using System.Web.Routing;
public class _Page_Views_Home_Index_cshtml : System.Web.Mvc.WebViewPage<string[]> {
public _Page_Views_Home_Index_cshtml() { }
public override void Execute() { WriteLiteral("\r\n");
ViewBag.Title = "Index";
WriteLiteral("\r\nThis is a list of fruit names:\r\n\r\n");
foreach (string name in Model) { WriteLiteral(" <span><b>");
Write(name);
WriteLiteral("</b></span>\r\n");
} } } }
The first thing to note is that the class is derived from WebViewPage<T>, where T is the model type – this is how strongly-typed views are handled. The second thing to note is the name of the class that has been generated – notice how the path of the view file has been encoded in the class name. This is how Razor maps requests for views into instances of compiled classes.
In the Execute method, we can see how the statements and elements in the view have been handled. The code fragments that we prefixed with the @ symbol are expressed directly as C# statements. The HTML elements are handled with the WriteLiteral method, which writes the contents of the parameter to the result as they are – this is opposed to the Write method which is used for C# variables, which encodes the string values to make them safe for use in an HTML page.
Both the Write and WriteLiteral methods write content to a TextWriter object – this is the same object that is passed to the IView.Render method, which we saw at the start of the chapter. The goal of a compiled Razor view is to generate the static and dynamic content and send it to the client via the TextWriter – this is useful to know when we come to look at HTML helper methods later in the chapter.
Adding Dependency Injection to Razor Views
Every part of the MVC request processing pipeline supports dependency injection, and the Razor view engine is no exception – although the technique is different to what we have seen before because we are spanning the boundary between classes and views.
Let’s imagine that we have an interface for a simple calculator feature that will sum two numeric values. Our interface for this is called ICalculator and is shown in Listing 8.
Listing 8. The ICalculator interface namespace Views.Models { public interface ICalculator { int Sum(int x, int y);
} }
If we want a view to be able to use the ICalculator interface and have the implementation injected, then we need to create an abstract class that is derived from WebViewPage, as shown in Listing 9.
Listing 9. Deriving a class from WebPageView
using System.Web.Mvc;
using Ninject;
namespace Views.Models.ViewClasses {
public abstract class CalculatorView : WebViewPage {
[Inject]
public ICalculator Calulator { get; set; } }
}
The simplest way to support dependency injection is to use property injection, where our DI container injects dependencies into a property, rather than the constructor. To do this, we must annotate the property we want injected with the Inject attribute, as the listing
demonstrates. To take advantage of this in a view, we have to use the Razor inherits element, as shown in Listing 10.
Listing 10. Specifying inheritance in a Razor view
@inherits Views.Models.ViewClasses.CalculatorView
@{
ViewBag.Title = "Calculate";
}
<h4>Calculate</h4>
The calculation result for @ViewBag.X and @ViewBag.Y is @Calulator.Sum(ViewBag.X, ViewBag.Y)
We can use @inherits to specify the base class we create in Listing 7. This gives us access to the Calculator property, which we are then able to get in order to receive an
implementation of the ICalculator interface, all without creating a dependency between the view and the ICalculator implementation.
The effect of the inherits tag is to change the base for the generated class. The effect of the inherits tag in Listing 10 is that the generated class for the view is defined like this:
public class _Page_Views_Home_Calculate_cshtml : Views.Models.ViewClasses.CalculatorView { ...
}
Since our generated class is now derived from our abstract class, we have access to the Calculator property we defined. All that remains is to register our implementation of the ICalculator interface with the dependency resolver so that Ninject will be used to create the view class and have the opportunity to perform the property injection. We do this in the AddBindings method of our NinjectDependendencyResolver class, as follows:
private void AddBindings() { // put bindings here
Bind<ICalculator>().To<SimpleCalculator>();
}
And, just like that, we have dependency injection for Razor views.
Configuring the View Search Locations
The Razor view engine follows the convention established in earlier versions of the MVC framework when looking for a view. If we have requested the Index view associated with the Home controller, then the list of views that Razor looks for is:
~/Views/Home/Index.cshtml
~/Views/Home/Index.vbhtml
~/Views/Shared/Index.cshtml
~/Views/Shared/Index.vbhtml
As we now know, Razor isn’t really looking for the view files on disk – they have already been compiled into C# classes. Razor looks for the compiled class that represents these views. The .cshtml files are templates containing C# statements (the kind we are using), while the .vbhtml files contain Visual Basic statements.
We can change the view files that Razor searches for by creating a subclass of
RazorViewEngine – this class is the Razor IViewEngine implementation. It builds on a series of base classes which define a set of properties that determine which views files are searched for – these properties are described in Table 1.
Table 1. Razor View Engine Search Property
Property Description Default Value
ViewLocationFormats MasterLocationFormats PartialViewLocationFormats
The locations to look for views, partial views and layouts.
"~/Views/{1}/{0}.cshtml",
"~/Views/{1}/{0}.vbhtml",
"~/Views/Shared/{0}.cshtml",
"~/Views/Shared/{0}.vbhtml"
AreaViewLocationFormats AreaMasterLocationFormats AreaPartialViewLocationFormats
The locations to look for views, partial views and layouts for an area.
"~/Areas/{2}/Views/{1}/{0}.cshtml",
"~/Areas/{2}/Views/{1}/{0}.vbhtml",
"~/Areas/{2}/Views/Shared/{0}.cshtml",
"~/Areas/{2}/Views/Shared/{0}.vbhtml"
These properties pre-date the introduction of Razor, which why there each set of three properties has the same values. Each property is an array of strings which are expressed using
the composite string formatting notation – the parameter values that correspond to the placeholders:
{0} – the name of the view {1} – the name of the controller {2} – the name of the area
To change the search locations, we create a new class that is derived from
RazorViewEngine and change the values for one or more of the properties described in Table 1.
Listing 11 demonstrates a replacement view engine which changes the name of the folder used for shared views and looks only for C# Razor templates (which have the .cshtml file extension).
Listing 11. Changing the search locations in the Razor view engine using System.Web.Mvc;
namespace Views.Infrastructure {
public class CustomRazorViewEngine : RazorViewEngine { public CustomRazorViewEngine() {
ViewLocationFormats = new string[] { "~/Views/{1}/{0}.cshtml",
"~/Views/Common/{0}.cshtml"
};
} } }
We have set a new value for the ViewLocationFormats – our new array contains entries only for .cshtml files. In addition, we have changed the location we look for shared views to be Views/Common, rather than Views/Shared.
We register our derived view engine using the ViewEngines.Engines collection in the Application_Start method of Global.asax, like this:
protected void Application_Start() { AreaRegistration.RegisterAllAreas();
ViewEngines.Engines.Clear();
ViewEngines.Engines.Add(new CustomRazorViewEngine());
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
Remember that the action invoker goes to each view engine in turn to see if a view can be found. By the time that we are able to add our view to the collection, it will already contain the standard Razor view engine. To avoid competing with that implementation we have called the Clear method to remove any other view engines that may have been registered and then called the Add method to register our custom implementation.
Adding Dynamic Content to a Razor View
The whole purpose of views is to allow us to render parts of our domain model as a user interface – to do that, we need to be able to add dynamic content to views. Dynamic content is generated at runtime and can be different for each and every request – this isas opposed to static content, such as HTML, which we create when we are writing the application and which is the same for each and every request. There are four ways that we can add dynamic content to a view, which are described in Table 2.
Table 2. The five ways to add dynamic content to a view
Technique When to Use
Inline code Use for small, self-contained pieces of view logic, such as if and foreach statements. This is the fundamental tool for creating dynamic content in views on which some of the other approaches are built.
HTML Helper Methods Use to generate single or small collections of HTML elements, typically based on view model or view data values. The MVC framework includes a number of useful HTML helper methods and it is easy to create your own – as we’ll show you, any method that returns an MvcHtmlString object can be an HTML helper method.
Partial Views Use for sharing subsections of view markup between views. Partial views can contain inline code, HTML helper methods and references to other partial views. Partial views don’t invoke an action method, so they can’t be used to perform business logic.
Child Actions Use for creating reusable UI controls or widgets that need to contain business logic. When you use a child action, it invokes an action method, renders a view and injects the result into the response stream.
Using Inline Code
The simplest and easiest way to generate dynamic content is to use inline code, which is to say one or more C# statements which are prefixed with the @ symbol. This is the heart of the Razor
view engine and we use this technique in almost all of the examples so far in this book. See Chapter 5 for details of the Razor syntax – we are not going to repeat that information in this chapter. Instead, we are going to demonstrate some advanced features that will make sense now that we explained more of the details about request processing and view engines.