Changing Base Type Of A Razor View

Within a Razor view, you have access to a base set of properties (such as Html, Url, Ajax, etc.) each of which provides methods you can use within the view.

For example, in the following view, we use the Html property to access the TextBox method.

@Html.TextBox("SomeProperty")

Html is a property of type HtmlHelper and there are a large number of useful extension methods that hang off this type, such as  TextBox.

But where did the Html property come from? It’s a property of System.Web.Mvc.WebViewPage, the default base type for all razor views. If that last phrase doesn’t make sense to you, let me explain.

Unlike many templating engines or interpreted view engines, Razor views are dynamically compiled at runtime into a class and then executed. The class that they’re compiled into derives from WebViewPage. For long time ASP.NET users, this shouldn’t come as a surprise because this is how ASP.NET pages work as well.

Customizing the Base Class

HTML 5 (or is it simply “HTML” now) is a big topic these days. It’d be nice to write a set of HTML 5 specific helpers extension methods, but you’d probably like to avoid adding even more extension methods to the HtmlHelper class because it’s already getting a little crowded in there.

html-extensions

Well perhaps what we need is a new property we can access from within Razor. Well how do we do that?

What we need to do is change the base type for all Razor views to something we control. Fortunately, that’s pretty easy. When you create a new ASP.NET MVC 3 project, you might have noticed that the Views directory contains a Web.config file.

Look inside that file and you’ll notice the following snippet of XML.

<system.web.webPages.razor>
    <host factoryType="System.Web.Mvc.MvcWebRazorHostFactory, 
    System.Web.Mvc, Version=3.0.0.0, 
    Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
  <pages pageBaseType="System.Web.Mvc.WebViewPage">
    <namespaces>
      <add namespace="System.Web.Mvc" />
      <add namespace="System.Web.Mvc.Ajax" />
      <add namespace="System.Web.Mvc.Html" />
      <add namespace="System.Web.Routing" />
    </namespaces>
  </pages>
</system.web.webPages.razor>

The thing to notice is the <pages> element which has the pageBaseType attribute. The value of that attribute specifies the base page type for all Razor views in your application. But you can change that value by simply replacing that value with your custom class. While it’s not strictly required, it’s pretty easy to simply write a class that derives from WebViewPage.

Let’s look at a simple example of this.

public abstract class CustomWebViewPage : WebViewPage {
  public Html5Helper Html5 { get; set; }

  public override void InitHelpers() {
    base.InitHelpers();
    Html5 = new Html5Helper<object>(base.ViewContext, this);
  }
}

Note that our custom class derives from WebViewPage, but adds a new Html5 property of type Html5Helper. I’ll show the code for that helper here. In this case, it pretty much follows the pattern that HtmlHelper does. I’ve left out some properties for brevity, but at this point, you can add whatever you want to this class.

public class Html5Helper {
  public Html5Helper(ViewContext viewContext, 
    IViewDataContainer viewDataContainer)
    : this(viewContext, viewDataContainer, RouteTable.Routes) {
  }

  public Html5Helper(ViewContext viewContext,
     IViewDataContainer viewDataContainer, RouteCollection routeCollection) {
    ViewContext = viewContext;
    ViewData = new ViewDataDictionary(viewDataContainer.ViewData);
  }

  public ViewDataDictionary ViewData {
    get;
    private set;
  }

  public ViewContext ViewContext {
    get;
    private set;
  }
}

Let’s write a simple extension method that takes advantage of this new property first, so we can get the benefits of all this work.

public static class Html5Extensions {
    public static IHtmlString EmailInput(this Html5Helper html, string name, 
string value) { var tagBuilder = new TagBuilder("input"); tagBuilder.Attributes.Add("type", "email"); tagBuilder.Attributes.Add("value", value); return new HtmlString(tagBuilder.ToString()); } }

Now, if we change the pageBaseType to CustomWebViewPage, we can recompile the application and start using the new property within our Razor views.

Html5Helpers

Nice! We can now start using our new helpers. Note that if you try this and don’t see your new property in Intellisense right away, try closing and re-opening Visual Studio.

What about Strongly Typed Views

What if I have a Razor view that specifies a strongly typed model like so:

@model Product
@{
    ViewBag.Title = "Home Page";
}

<p>@Model.Name</p>

The base class we wrote wasn’t a generic class so how’s this going to work? Not to worry. This is the part of Razor that’s pretty cool. We can simply write a generic version of our class and Razor will inject the model type into that class when it compiles the razor code.

In this case, we’ll need a generic version of both our CustomWebViewPage and our Html5Helper classes. I’ll follow a similar pattern implemented by HtmlHelper<T> and WebViewPage<T>.

public abstract class CustomWebViewPage<TModel> : CustomWebViewPage {
  public new Html5Helper<TModel> Html5 { get; set; }

  public override void InitHelpers() {
    base.InitHelpers();
    Html5 = new Html5Helper<TModel>(base.ViewContext, this);
  }
}

public class Html5Helper<TModel> : Html5Helper {
  public Html5Helper(ViewContext viewContext, IViewDataContainer container)
    : this(viewContext, container, RouteTable.Routes) {
  }

  public Html5Helper(ViewContext viewContext, IViewDataContainer container, 
      RouteCollection routeCollection) : base(viewContext, container,
      routeCollection) {
    ViewData = new ViewDataDictionary<TModel>(container.ViewData);
  }

  public new ViewDataDictionary<TModel> ViewData {
    get;
    private set;
  }
}

Now you can write extension methods of Html5Helper<TModel> which will have access to the model type much like HtmlHelper<TModel> does.

As usual, if there’s a change you want to make, there’s probably an extensibility point in ASP.NET MVC that’ll let you make it. The tricky part of course, in some cases, is finding the correct point.

What others have said

Requesting Gravatar... Martin Feb 21, 2011 4:21 AM
# re: Changing Base Type Of A Razor View
Why are the methods of Html5Helper implemented as extension methods instead of "normal" methods? Is there any advantage?
Requesting Gravatar... Jesse Feb 21, 2011 5:05 AM
# re: Changing Base Type Of A Razor View
I ran into this exact problem doing, literally, the exact same thing a few weeks ago. Now I know the solution, but my cool idea for some separate HTML5 helpers doesn't seem so cool. So...thanks? :)
Requesting Gravatar... Macu Feb 21, 2011 5:07 AM
# re: Changing Base Type Of A Razor View
There is a problem with this special web.config file hidden in view directory. If you try to plug mvc directly into source codes some of the razor intelisence will break. (set PublicKeyToken of MvcWebRazorHostFactory to null and plug your own Razor dlls ) . I other words try not to define your extensibility point in mvc soruce codes or you will end up without intelisence support.
Best
Requesting Gravatar... haacked Feb 21, 2011 6:58 AM
# re: Changing Base Type Of A Razor View
@Martin they could have been normal methods. It's just an example of how I can add more methods later without changing the Html5Helper class.
Requesting Gravatar... xgluxv Feb 21, 2011 10:02 AM
# re: Changing Base Type Of A Razor View
Can we change base type of a Razor view with code ,not change web.config ?
Requesting Gravatar... Jimmy Bogard Feb 21, 2011 1:56 PM
# re: Changing Base Type Of A Razor View
Cool stuff. How is the generic type chosen? Does it need to have the same name as the non-generic version, or is there additional web.config changes needed?
Requesting Gravatar... Koistya `Navin Feb 21, 2011 10:49 PM
# re: Changing Base Type Of A Razor View
Good hint. Going to use it :)
Requesting Gravatar... haacked Feb 22, 2011 12:48 AM
# re: Changing Base Type Of A Razor View
@xgluxv No, I don't believe so.

@Jimmy I believe it goes by name because I didn't need to make any additional web.config changes. But I'll follow up to find out for sure.
I confirmed, it is indeed by name. In C#, you actually always need the generic type because even without a model, the view is typed as dynamic by default.
Requesting Gravatar... Nathan Feb 22, 2011 2:38 AM
# re: Changing Base Type Of A Razor View
Out of curiosity, can something similar be done with WebMatrix and System.Web.WebPages.WebPage?
Requesting Gravatar... Allan Ferreira Feb 22, 2011 4:01 AM
# re: Changing Base Type Of A Razor View
Great article! Any insights about how the Execute method works? I mean, when in the pipeline does the class gets created (inheriting from the class defined in the web.config) and when does the method Execute itself created/called?
Requesting Gravatar... Tim Scott Feb 22, 2011 12:39 PM
# re: Changing Base Type Of A Razor View
There certainly is an advantage to extending the class instead of a property of it. For the big payoff, go one step further and extend only interfaces.

Case in point, MvcContrib.FluentHtml. Some years ago, we created a whole bunch of helpers. The helpers extend the existing MVC framework interface. We added our own interface as well and extended it.

And the payoff? All of FluentHtml extensions are available in Razor by adding one very small class. It took like 30 minutes.
Requesting Gravatar... Kourosh Saleh Feb 23, 2011 9:37 PM
# re: Changing Base Type Of A Razor View
Hi,
Thank you for your real valuable blog.

I am newbie on MVC and trying to learn.
when I try your solution I got following error.
The type or namespace name 'CustomWebVeiwPage' does not exist in the namespace 'MvcApplication1' (are you missing an assembly reference?)
thank you again.
Requesting Gravatar... ReFocus Feb 24, 2011 11:38 PM
# re: Changing Base Type Of A Razor View
Is it possible to create your own implementation of the MvcWebRazorHostFactory and inject code into the views?
Requesting Gravatar... Thanigainathan Feb 27, 2011 1:29 PM
# re: Changing Base Type Of A Razor View
Hi,

One thing that bothers me in this article is that Razor views are dynamically compiled.Will this be a performance impact ?

Thanks,
Thani
Requesting Gravatar... Jonas Eriksson Mar 03, 2011 2:11 AM
# re: Changing Base Type Of A Razor View
Is it possible to add a separate web.config in a subfolder with just that section and a base class for the cshtml's in that folder?
Requesting Gravatar... Jonas Mar 03, 2011 2:30 AM
# re: Changing Base Type Of A Razor View
Do'h, of course it does. *delete* *won't post again when I'm too tired*
Requesting Gravatar... haacked Mar 04, 2011 12:32 PM
# re: Changing Base Type Of A Razor View
@Kouroush Looks like you mispelled it. It should be CustomWebViewPage not CustomWebVeiwPage.

@Nathan, I'm not sure.

@Allan, the class is created and compiled very early in the pipeline once. The execute method is called much later.

@Refocus Yes, you can. Takes a bit of work though.

@Thanigainathan It's only a perf hit on the first request. After that, it's very fast because it's all compiled.
Requesting Gravatar... Andrew Nurse Mar 07, 2011 3:07 AM
# re: Changing Base Type Of A Razor View
@Thanigainathan - Just to follow up on Phil's answer: Razor uses exactly the same compilation model as ASPX, so the performance after the first request is very similar to ASPX. Razor does take a little more time to do that initial compilation due to it being a more complex language to parse, but that's only on the first request.
Requesting Gravatar... Berlin dong Mar 07, 2011 7:10 PM
# re: Changing Base Type Of A Razor View
CustomWebViewPage<TModel> should derive from WebViewPage<TModel>, not WebViewPage.
Requesting Gravatar... Mare Mar 31, 2011 11:01 PM
# re: Changing Base Type Of A Razor View
Macu pointed out to the problems with intellisense when changing the web.config file in Views folder and I can confirm it. It makes everything to break (no other helpers are recognized/red underlined) and our custom helpers are not available though they do work when you run the site. It's just in the VS that they get broken.

Could you please investigate on that?
Requesting Gravatar... Mark Apr 08, 2011 9:09 AM
# re: Changing Base Type Of A Razor View
This is the coolest tip I've run into in several weeks! Thanks for sharing it.

BTW, I don't know if anyone else noticed, but unless you define your custom class in the same namespace as the MVC default intellisense breaks down.
Requesting Gravatar... DrMsal Apr 24, 2011 2:07 AM
# re: Changing Base Type Of A Razor View
Nice Idea but its not working...

Extension method is not showing up. its not even accessible. here is code I am using..

public abstract class CustomWebViewPage : WebViewPage
{

public Html5Helper Html5 { get; set; }

public override void InitHelpers()
{
base.InitHelpers();
Html5 = new Html5Helper(base.ViewContext, this);
}
}
}
++++++++++++++++++++
public class Html5Helper
{
public Html5Helper(ViewContext viewContext,
IViewDataContainer viewDataContainer)
: this(viewContext, viewDataContainer, RouteTable.Routes)
{
}

public Html5Helper(ViewContext viewContext,
IViewDataContainer viewDataContainer, RouteCollection routeCollection)
{
ViewContext = viewContext;
ViewData = new ViewDataDictionary(viewDataContainer.ViewData);
}

public ViewDataDictionary ViewData
{
get;
private set;
}

public ViewContext ViewContext
{
get;
private set;
}
}
+++++++++++++++++++++++ And Extension Method +++++++++++
public static class Html5Extensions
{
public static IHtmlString GetMyName(this Html5Helper html)
{
var tagBuilder = new TagBuilder("span");
tagBuilder.SetInnerText("Hello World");
return new HtmlString(tagBuilder.ToString());
}
}


Requesting Gravatar... DrMsal Apr 24, 2011 4:26 AM
# re: Changing Base Type Of A Razor View
Got it finally.
when you set the base type in webconfig file it set itself as strongly type by default. So I put @inherits directive on view itself make non strongly type. that make it work.

Is it correct way?? or is there any other better way of doing the same ???
Requesting Gravatar... Sam Moore Jul 05, 2011 4:03 AM
# re: Changing Base Type Of A Razor View
Hey Phil,
I've been playing around with this recently and I ran into a bit of an issue. What do I do when I want to use this functionality across multiple ASP.NET "Areas"? Do I have to make this change in each web.config in each area? Or can I just make it in the root section and delete the other web.configs?
Requesting Gravatar... John Jul 30, 2011 2:45 PM
# re: Changing Base Type Of A Razor View
Super neat way to go and would like to use it. Any chance that you could post a basic working set of code?

Pretty sure that I've followed everything written here and am getting a compilation error, not on build but when browsing a view:

CS0308: The non-generic type 'xxx.xxxx.CustomWebViewPage' cannot be used with type arguments.

Source Error:
public class _Page_Views_TestCheckout_Index_cshtml : xxx.xxxx.CustomWebViewPage<dynamic> {
Requesting Gravatar... Andy Cohen Dec 02, 2011 10:04 PM
# re: Changing Base Type Of A Razor View
Phil,

At the beginning of this great article, you said:

"Html is a property of type HtmlHelper and there are a large number of useful extension methods that hang off this type, such as TextBox."

Don't you mean that it is a property of type WebViewPage?
Requesting Gravatar... Andy Cohen Dec 02, 2011 10:07 PM
# re: Changing Base Type Of A Razor View
Whoops... I see what you meant, it is of type HtmlHelper<Tmodel> but it is a property of the class WebViewPage<TModel>.
Requesting Gravatar... haacked Dec 03, 2011 1:44 AM
# re: Changing Base Type Of A Razor View
@Andy exactly! :)
Requesting Gravatar... Ben Dec 30, 2011 7:54 PM
# re: Changing Base Type Of A Razor View
I'm trying this technique with the RazorEngine, and I'm getting an error every time. Basically it will say "Unable to compile template. The name 'Html5Helper' does not exist in the current context".

I'm a bit stumped, ATM. Any advice would be helpful.
Requesting Gravatar... Ben Dec 30, 2011 8:00 PM
# re: Changing Base Type Of A Razor View
I found the issue, actually. It appears the RazorEngine does not yet support these sorts of properties (Html, Url, etc).

stackoverflow.com/.../razorengine-issues-with-html

I hope this helps someone else.
Requesting Gravatar... Matthew Marksbury Jan 24, 2012 9:47 PM
# re: Changing Base Type Of A Razor View
This article helped me a lot, thanks. There is one problem with the code sample, however, and that is the base class on CustomWebViewPage<TModel>. It is shown as CustomWebViewPage, which compiles, but throws a runtime error when you load a model bound page. The only solution to this, that I could find was having multiple CustomWebView pages that derive from WebViewPage and WebViewPage<T>.
Requesting Gravatar... Evan Larsen Feb 10, 2012 7:14 PM
# re: Changing Base Type Of A Razor View
Thanks a lot Phil! I was looking everywhere on how to do this. I ended up using ILSpy on the RazorViewEngine to figure out that its initializing the helpers in the WebViewPage. After finding the correct terminology and using Bing. I found this page. Exactly what I was looking for. Thanks again.

What do you have to say?

(will show your gravatar)
Please add 6 and 1 and type the answer here: