Razor Preprocessed Templates

08 Dec 2012

When Miguel asked me to investigate making
MonoDevelop support using
Razor
templates in MonoTouch and Mono for
Android apps, I realized that it could be
done simply and with very few dependencies by taking the same approach as T4
preprocessed templates,
which I implemented for MonoDevelop a couple of years ago.
Fortunately, this time the hard part was already done: I could use Microsoft’s
open-source Razor parser instead of writing
my own parser. I also found a Visual Studio extension called Razor
Generator that was very close in
functionality to what I wanted, and was able to use this as a basis for my work.
I was able to hook it it into the fantastic Razor editing and code completion
support written by Piotr Dowgiallo in the Google
Summer of Code this year.

After a few days work implementing, tweaking and tuning (and bouncing ideas off
Bojan Rajković), I present Razor
Preprocessed Templates in MonoDevelop.

As a basis for this demo, I created new a MonoTouch iPhone Single View
application, added a UIWebView to the View’s xib, and connected it to an outlet
on the controller called csharpwebview. However, you can use these templates
in any project type.

Just add a new Text Templating -> Preprocessed Razor Template file to the project:

You will see that this adds a cshtml Razor C# file to the project, grouped with
a C# file that contains the generated code. Like T4 files, this uses the “custom
tool” extensibility mechanism. By setting the custom tool property on the
cshtml file set to “RazorTemplatePreprocessor”, it causes MonoDevelop to use
this new custom tool to regenerate the cs file whenever the cshtml file is
saved.

I wrote a simple Razor page to demonstrate the power of Razor. It uses a simple
Razor helper to demonstrate that Razor helpers work correctly. The page also
demonstrates using the @model directive to specify the type for a Model
property, which easily allows us to pass data into the template before running
it. Since this demo is very simple, I just used an int as the model instead of
defining a proper model class.

When writing this, the Razor code completion was very helpful. It has full C#
completion, including locals, helpers and members from the generated class and
base class, including the generated Model property:

There’s also HTML completion and on-the-fly underlining of HTML and Razor errors:

After saving the cshtml file, you can look at the generated cs file. It’s
pretty messy, so I won’t show it here, but note that it includes a
well-commented generated base class. If you want, you can specify a base class
using the @inherits directive, so you can pull that generated base class out
and share it between multiple templates, or customize it. The template’s only
dependency is Action<System.IO.TextWriter>, and the default base class’s
dependencies are only System.Web.HttpUtility.HtmlEncode(string) and
System.IO.StringWriter, so it can easily be made to run anywhere. If your
target framework lacks the one potentially awkward dependency,
HttpUtility.HtmlEncode(string), you can provide an alternative implementation
via a custom base class.

This is a fantastic way to generate HTML pages without pulling in the whole
System.Web stack, and I hope you’re as excited about it as I am. It isn’t
available today, unless you build MonoDevelop from source, however the code is
committed to MonoDevelop master and should make
it into the next 3.x release.