Using MVC HtmlHelper extensions from Razor declarative views
I was trying to create a Razor declarative helper in my App_Code folder for an MVC 3 RTM project.
The problem I ran into was that the MVC HtmlHelper extensions, like ActionLink, aren't available. This is because the compiled helpers derive from System.Web.WebPages.HelperPage
, and though it exposes an Html
property, its of type System.Web.WebPages.HtmlHelper
rather than System.Web.Mvc.HtmlHelper
.
An example of the kind of error I was getting is:
'System.Web.Mvc.HtmlHelper' does not contain a definition for 'ActionLink' and no extension method 'ActionLink' accepting a first argument of type 'System.Web.Mvc.HtmlHelper' could be found (are you missing a using directive or an assembly reference?)
My only solution has been to create my own HelperPage and override the Html property:
using System.Web.WebPages;
public class HelperPage : System.Web.WebPages.HelperPage
{
// Workaround - exposes the MVC HtmlHelper instead of the normal helper
public static new HtmlHelper Html
{
get { return ((System.Web.Mvc.WebViewPage) WebPageContext.Current.Page).Html; }
}
}
I then have to write the following at the top of every helper:
@inherits FunnelWeb.Web.App_Code.HelperPage
@using System.Web.Mvc
@using System.Web.Mvc.Html
@helper DoSomething()
{
@Html.ActionLink("Index", "Home")
}
Is it meant to be this hard in MVC 3, or am I doing something wrong?
Take a look at Marcind
's answer to this question. What you're experiencing is a limitation of putting declarative views in the App_Code
folder.
Putting your helpers in App_Code works but has certain limitations that impact certain MVC scenarios (for example: no access to standard MVC Html. helpers)
I created an extension method for the WebPages helper so that I can get access to the page helper.
public static HtmlHelper GetPageHelper(this System.Web.WebPages.Html.HtmlHelper html)
{
return ((System.Web.Mvc.WebViewPage) WebPageContext.Current.Page).Html;
}
Omar's got the right answer here, but I wanted to add something (do feel free to mark Omar's response as the answer).
We were aware of this in v1 and weren't able to get a great fix in the product, but David Ebbo (an architect on the ASP.Net team) posted a sample of a Visual Studio Code Generator that is basically a first exploration of the kind of ideas we're looking at to make this work properly: http://blogs.msdn.com/b/davidebb/archive/2010/10/27/turn-your-razor-helpers-into-reusable-libraries.aspx
Try that out and see what you think! Let David know if you have comments by posting on his blog.
Similar to @Jakes answer:
public static class MvcIntrinsics {
public static System.Web.Mvc.HtmlHelper Html {
get { return ((System.Web.Mvc.WebViewPage)WebPageContext.Current.Page).Html; }
}
public static System.Web.Mvc.AjaxHelper Ajax {
get { return ((System.Web.Mvc.WebViewPage)WebPageContext.Current.Page).Ajax; }
}
public static System.Web.Mvc.UrlHelper Url {
get { return ((System.Web.Mvc.WebViewPage)WebPageContext.Current.Page).Url; }
}
}
Usage:
@MvcIntrinsics.Html.Raw("test")
Source: Dino Esposito - Programming Microsoft ASP.NET MVC
An alternative solution:
Add this on top of your razor-helper file:
@functions {
public static System.Web.Mvc.HtmlHelper<object> HHtml = ((System.Web.Mvc.WebViewPage)WebPageContext.Current.Page).Html;
}
then call it like this:
@HHtml.ActionLink("actionname")