ASP.NET MVC: Creating Custom View Engine

ASP.NET MVC started off with the premise of being a very ‘pluggable’ framework and we can see it almost everywhere. You can plug in any IoC container, Testing Framework even View Engines! There are two view engines provided by default - the WebForms View engine (views with .aspx extention) and the Razor view engine (view with .cshtml extension).

Today we’ll see how the pluggability of view engines can be leveraged in our own web application.

We will create a new custom view engine for MVC applications. This is just a very simple implementation that can be modified for your project requirements.

Internals of a View Engine

As we know, in controllers each action method has the return type of ‘ActionResult’. This is an abstract class which forms a basic implementation for various classes and one of the derived class from ActionResult is ‘ViewResult’. This class is used to render a View using the IView instance that is returned by an implementation of the IViewEngine. This means that if we want to create our custom view engine, we must use the IView and IViewEngine interfaces.


The interface IView provides a Render method. This is responsible for defining how the View is rendered. This method accepts the first argument as ‘ViewContext’. This object contains the information that is related for view rendering. Technically, from this object, the Model object containing the data to be displayed in the view can be extracted. The second object is the TextWriter which defines the HTML format strings to be used during rendering.

The interface IViewEngine defines methods for View Engine. This provides methods as below:
  • FindView: Finds the specific view (file) by using the specified controller context.
  • FindPartialView: Find the specific partial view by using the specified controller context.
  • ReleaseView: Releases the specified view by using the specified controller context.
Here the controller context means the information in the HTTP request that matches route expression which contains the controller name and the action method in the controller.

Building the ‘Report Engine’

Step 1: Open Visual Studio 2012 and create a new MVC application. Name it as ‘MVC40_CustomViewEngine’. In the Models folder, add a new class file, name it as ‘DataClasses’.

Add the following code in it:

namespace MVC40_CustomViewEngine.Models
{
public class EmployeeInfo
{
  public int EmpNo { get; set; }
  public string EmpName { get; set; }
  public string DeptName { get; set; }
  public string Designation { get; set; }
  public int  Salary { get; set; }
}
public class DataAccess
{
  List<EmployeeInfo> lstEmps = new List<EmployeeInfo>();
  public DataAccess()
  {
   lstEmps.Add(new EmployeeInfo() {EmpNo=1,EmpName="A",DeptName="D1",Designation="TL", Salary=45000 });
   lstEmps.Add(new EmployeeInfo() { EmpNo = 2, EmpName = "B", DeptName = "D1", Designation = "TL", Salary = 45000 });
   lstEmps.Add(new EmployeeInfo() { EmpNo = 3, EmpName = "C", DeptName = "D2", Designation = "PM", Salary = 55000 });
   lstEmps.Add(new EmployeeInfo() { EmpNo = 4, EmpName = "D", DeptName = "D2", Designation = "PM", Salary = 55000 });
   lstEmps.Add(new EmployeeInfo() { EmpNo = 5, EmpName = "E", DeptName = "D3", Designation = "PH", Salary = 65000 });
   lstEmps.Add(new EmployeeInfo() { EmpNo = 6, EmpName = "F", DeptName = "D3", Designation = "PH", Salary = 65000 });
   }
  public List<EmployeeInfo> GetEmps()
  {
   return lstEmps;
  }
}
}

Step 2: In the MVC project, add a new folder, name it as ‘CustomViewEngine’. Add the class file of name DataView.cs in it. Write the following code in it:

using MVC40_CustomViewEngine.Models;
using System.Collections.Generic;
using System.Web.Mvc;

namespace MVC40_CustomViewEngine.CustomViewEngine
{
/// <summary>
/// Class for defining the actual View. This class is actual responsible for defining the HTML layouting
/// of the view.
/// </summary>
public class DataView : IView
{
  /// <summary>
  /// The Method used to Render the HTML
  /// </summary>
  /// <param name="viewContext"></param>
  /// <param name="writer"></param>
  public void Render(ViewContext viewContext, System.IO.TextWriter writer)
  {
   //Step 1 => Get the Model Bind with the View. (Note, this value is received from the controller)
   var Model = viewContext.ViewData.Model;
   var Emps = Model as List<EmployeeInfo>;
   //Step 2 => Now Geterate the HTML Table
   writer.Write("<table border=1><tr><th>EmpNo</th><th>EmpName</th><th>
   DeptName</th><th>Designation</th><th>Salary</th></tr>");
   foreach (EmployeeInfo Emp in Emps)
   {
   writer.Write("<tr><td>"+Emp.EmpNo+"</td><td>"+Emp.EmpName+"</td><td>"+
   Emp.DeptName+"</td><td>"+Emp.Designation+"</td></tr>");
   }
   writer.Write("</table>");
  }
}
}

The DataView class implements the IView interface and implements its ‘Render’ method. The method reads the model object received from the controller. The TextWriter instance is used to create the HTML table for displaying the information.

Step 3: In the folder created in Step 2, add a new class file of name ‘DataViewEngine’. Add the following code in it:

using System;
using System.Web.Mvc;

namespace MVC40_CustomViewEngine.CustomViewEngine
{
/// <summary>
/// The class Implementing 'IViewEngine' interface. This Interface  methods for ViewEngine
/// </summary>
public class DataViewEngine : IViewEngine
{
  /// <summary>
  /// Method for Partial Viuew
  /// </summary>
  /// <param name="controllerContext"></param>
  /// <param name="partialViewName"></param>
  /// <param name="useCache"></param>
  /// <returns></returns>
  public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
  {
   throw new NotImplementedException();
  }

  /// <summary>
  /// Method for View
  /// </summary>
  /// <param name="controllerContext"></param>
  /// <param name="viewName"></param>
  /// <param name="masterName"></param>
  /// <param name="useCache"></param>
  /// <returns></returns>
  public ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
  {
   if (viewName == "DataView")
   {
    return new ViewEngineResult(new DataView(), this);
   }
   else
   {
    return new ViewEngineResult(new string[] {"This View Engine is Currently in design, will be launched soon"});
   }
  }

  /// <summary>
  /// Release the view
  /// </summary>
  /// <param name="controllerContext"></param>
  /// <param name="view"></param>
  public void ReleaseView(ControllerContext controllerContext, IView view)
  {
  }
}
}

  
The class DataViewEngine, implements IViewEngine interface and implements its FindView method. This method checks the ViewName specified by the controller’s action method and if the name matches it returns an object of ViewEngineResult object. This object is responsible for locating the IView typed object, in this case it is DataView class.

If you provided markup files for providing design-time markup, the FindView and the FindPartialView methods are the points to load the markup file.

Step 4: Open the Global.asax.cs and register the view engine in Application_Start method as below:

ViewEngines.Engines.Add(new DataViewEngine());

Step 5: In the controller folder, add a new controller of name EmployeeInfoController and add the following code in the index method:

public ActionResult Index()
{
var Emps = new DataAccess().GetEmps();
ViewData.Model = Emps;
return View("DataView");
}

  
The method returns the ViewResult by accepting the View name as string. The MVC view engine matches this string with the Custom View class and executes the corresponding Render method.
Run the application and navigate to the EmployeeInfo/Index the result will be as below:

custom-view
It shows the HTML table with Employee data in it.

Conclusion

As we saw, building a custom view engine is pretty easy in ASP.NET MVC. It is one of the heavily used extension feature in MVC.

Building reporting engines to create reports as we saw above, is just one such use case.

Download the entire source code (Github)




About The Author

Mahesh Sabnis is a Microsoft MVP having over 18 years of experience in IT education and development. He is a Microsoft Certified Trainer (MCT) since 2005 and has conducted various Corporate Training programs for .NET Technologies (all versions). He also blogs regularly at DotNetCurry.com. Follow him on twitter @maheshdotnet

3 comments:

Timon Mcdonald said...

New Version asp.net 4 contributes a few new functions such as bundling and minification of program information, functions for asynchronous programming. However, all of these are upgrades to the ASP.NET system as a whole.

Burke Haynes said...

Using MVC you can easily build web application.MVC uses a standard directory structure and file naming standards which are a very important part of MVC application development.

Anonymous said...

how would you unit test your view engine and DataView?