Using MVC HtmlHelper extensions from Razor declarative views

asp.net Mvcasp.net Mvc-3Razor

asp.net Mvc Problem Overview


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?

asp.net Mvc Solutions


Solution 1 - asp.net Mvc

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)

Solution 2 - asp.net Mvc

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;
}

Solution 3 - asp.net Mvc

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.

Solution 4 - asp.net Mvc

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

Solution 5 - 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")

Solution 6 - asp.net Mvc

For the benefit of searchers, I got the same error when creating MVC views as part of a class library (for component re-use). The solution, partially alluded to above, was to add the following using statements at the top of the .cshtml file:

@using System.Web.Mvc
@using System.Web.Mvc.Html

No further work necessary.

Solution 7 - asp.net Mvc

My approach to this is to simply pass the page as a parameter to the helper method. So in your example it would be:

@helper DoSomething(WebViewPage page)
{
    @page.Html.ActionLink("Index", "Home")
}

Then in your Razor view where you need it call it like this:

@YourHelperFilename.DoSomething(this)

Doing this immediately gives you access to page properties like Html or Url that you usually have (and through that the HtmlHelper extensions).

As an additional benefit (if you require this), you also get access to instance properties such as the page's ViewData.

Solution 8 - asp.net Mvc

I know that there are some intellisense issues with MVC 3. I think the helpers will still work if you have the namespace set in web.config.

MVC 3 RTM has just been realeased are you using this or a beta?

Solution 9 - asp.net Mvc

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPaul StovellView Question on Stackoverflow
Solution 1 - asp.net MvcOmarView Answer on Stackoverflow
Solution 2 - asp.net MvcJake HoffnerView Answer on Stackoverflow
Solution 3 - asp.net MvcAndrew Stanton-NurseView Answer on Stackoverflow
Solution 4 - asp.net MvcGreg GumView Answer on Stackoverflow
Solution 5 - asp.net MvcAlex from JitbitView Answer on Stackoverflow
Solution 6 - asp.net MvcJsAndDotNetView Answer on Stackoverflow
Solution 7 - asp.net MvcDejanView Answer on Stackoverflow
Solution 8 - asp.net MvcLee SmithView Answer on Stackoverflow
Solution 9 - asp.net MvcOmarView Answer on Stackoverflow