Modifying Code Generation Templates for MvcContrib Grid

Visual Studio 2010 in general, and MVC 2 in particular, bring the concept of code generation templates into the realm of first-class objects. Well, maybe it's not that good. However, MVC definitely makes tremendous use of the code generation template system, called Text Transformation Template Toolkit (or T4 for short). A fantastic introduction to T4 editing can be found in the January 2010 issue of MSDN Magazine, titled "Text Template Transformation Toolkit and ASP.NET MVC." That article covers the basics of editing T4 templates, so I won't bother repeating the same information. Instead, I'll describe the template I created for our team's latest project.

Before I get to the template itself, I need to describe the tools we're using, how to install them, and a little background on why we decided to do what we're doing.

The tools

MvcContrib Grid

Early on, we did what most teams do. We used the tools provided. For creating a list of strongly-typed objects (users, items, etc.), we simply chose the default "List" template that ships with MVC 2. Sure, it made our jobs easy, but I wasn't a huge fan of the HTML it generated.

Essentially, the List template uses reflection to look at the public properties of your model, and creates a simple HTML table using the raw property names as column headers. It also adds links to each row for "edit," "details," and "delete" actions. After the table's close tag, a handy link is provided to your "add" action.

About a month or so into our project, we discovered the MvcContrib project and its Grid helper. After experimenting with it for a few days, we decided that we preferred it to the default List view code, and that would use it exclusively for generating all of our List views.

At its simplest, given a strongly-typed Model, it will generate a grid very similar to what the default template gives you with a single line of code:


For us, the concise and simple View made it a no-brainer for us. From that time forward, we simply created an empty strongly-typed View and then added the line above, any additional ActionLinks we needed, and we were done.

I'm nothing if not lazy, so I wanted to see what it would take to avoid even typing that one line above! I remembered reading the T4 article mentioned above, and thought I'd spend some time trying to make a new template that would do the work for me. Of course, if something is worth doing, then someone has probably already done it.

In this case, it was done more than a year ago, by a gentleman named Shiju Varghese. In his post titled, "Add a new T4 template for making MVCContrib Grid Helper Component," he describes how to alter the default List template into a new one that uses the MvcContrib grid. Perfect! I decided to start there.

tangible T4 Editor

So now I needed a way to edit the .tt files. Sure, I could open them as raw text file, but where's the fun in that? If you're running Visual Studio 2008 or 2010, there is a great free add-on called the tangible T4 Editor. Essentially, it provides syntax coloring and IntelliSense to Visual Studio while editing the .tt files. So I visited the download site and installed the add-on.

Editing the List Template

Once the template editor was installed, I had to find the existing List template and modify it. This turned out to be pretty straightforward. In my case, the MVC view templates can be found at C:\Program Files\Microsoft Visual Studio 10.0\Common7\IDE\ItemTemplates\CSharp\Web\MVC 2\CodeTemplates\AddView. On a 64-bit machine, check C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\ItemTemplates\CSharp\Web\MVC 2\CodeTemplates\AddView. The template that I wanted to edit is "," which I copied into the same directory and immediately renamed it to "" to differentiate it from the original.

The really cool thing about this is that Visual Studio automatically recognizes it displays it in the Add View dialog.

One question you may be asking is, "If you already have the template, why do you still want to edit it?" One problem with both the default List template and Shiju's grid template is that neither honors the DataAnnotations on your view model. Specifically, our project requires us to support multiple languages. Simply iterating over the model's properties and blindly displaying the property name in the column header is not an option for us. We need to ensure that the column headers are localized. Furthermore, there may be columns we don't want to display at all. Fortunately, later versions of the MvcContrib Grid make this very simple.

So starting with Shiju's code as an example, I made my template look like this.

// See if the model has primary keys and generate appropriate ActionLinks for Edit and Delete.
List primaryKeys = GetEntityKeyProperties(mvcHost.ViewDataType);
String editAction = String.Empty;
String deleteAction = String.Empty;

if(primaryKeys.Count > 0) {
 editAction = String.Format("Html.ActionLink(\"Edit\", \"Edit\", new {{ id=x.{0} }})", primaryKeys[0]);
 deleteAction = String.Format("Html.ActionLink(\"Delete\", \"Delete\", new {{ id=x.{0} }})", primaryKeys[0]);
} else {
 editAction = "Html.ActionLink(\"Edit\", \"Edit\", new { /* id=x.PrimaryKey */ })";
 deleteAction = "Html.ActionLink(\"Delete\", \"Delete\", new { /* id=x.PrimaryKey */ })";

 Dictionary properties = new Dictionary();
 FilterProperties(mvcHost.ViewDataType, properties);
 <%<#=nugget#> Html.Grid< <#=mvcHost.ViewDataType#> >(Model).AutoGenerateColumns()
  .Empty("There are no records.")
(Download the entire TT file here)

Notice that I'm using AutoGenerateColumns(), and then adding the additional columns afterward. The beauty of this approach is that AutoGenerateColumns() honors the various attributes in the System.ComponentModel.DataAnnotations namespace. All of the display decisions (order, template, localized header name) can be handled in a declarative manner in the view model. For example, [DisplayName] and [ScaffoldColumn] on your view model's properties will automatically be reflected in the View. Likewise, any changes to your view model will be handled automatically when your project is rebuilt.

There is a known issue with using [DisplayName] that I won't go into here, except to say that if you're using MVC 2 with ASP.NET 4, you have to subclass the DisplayNameAttribute to get it to work. This is an issue with .NET, and not with the MvcGrid, and does not affect the use of the template.

Additional Enhancements

You may have noticed that I didn't localize the edit and delete ActionLinks, nor did I bother with an add ActionLink. I am still trying to come up with a generic way to handle that in a way that's still localizable. One thought I had is to access a properties on the view model called "AddTitle," "EditTitle," and "DeleteTitle." These could be read-only properties on a view model base class that simply returns the appropriate localized resource. I may end up taking that approach, but it still means infusing my template with domain knowledge. Maybe that's not a horrible thing. I'd love to hear comments from others who have run into the same problem.


Popular posts from this blog

How to copy your Frozen Free Fall progress to a new phone

Ionic vs. Bootstrap - for a Web App

How I Finally Got AdMob and Ionic Framework to Play Nice Together