Why is the HtmlHelper instance null in a Razor declarative @helper method?

a 夏天 提交于 2019-11-29 00:58:02
Darin Dimitrov

That's a known limitation of those helpers. One possibility is to pass it as parameter:

@helper TestHelperMethod(HtmlHelper html) {
    var extra = "class=\"foo\"";
    <div@html.Raw(extra)></div>
}

Another possibility is to write the helper as an extension method:

public static class HtmlExtensions
{
    public static MvcHtmlString TestHelperMethod(this HtmlHelper)
    {
        var div = new TagBuilder("div");
        div.AddCssClass("foo");
        return MvcHtmlString.Create(div.ToString());
    }
}

and then:

@Html.TestHelperMethod()
kkara

Using Drew Noakes suggestion, I have come to a workaround that does the trick for now and that can be easily removed once the issue is solved in a newer version of MVC (that is if more stuff isn't changed that would break it:))

The goal is to be able to use an HtmlHelper inside a declarative helper method that lives in a file in App_Code without having a NullReferenceException. To solve this I included in all the files in App_Code the following:

@using System.Web.Mvc;

@functions
{
    private static new HtmlHelper<object> Html
    {
        get { return ((WebViewPage)CurrentPage).Html; }
    }

    private static UrlHelper Url
    {
        get { return ((WebViewPage)CurrentPage).Url; }
    }
}

This does seem to do the trick as I can now write the following helper (in the same file):

@helper PrintAsRaw(string htmlString)
{
     @Html.Raw(htmlString)
}

Obviously the helper method is just an example. This solution has the downside that the @functions declarations has to be introduced in all helper declaration files in App_Code, but does avoid complicating the call to the helper, as you can simply write in a view:

@MyAppCodeFile.PrintAsRaw("<p>My paragraph</p>")

Hope this helps...

I know it's not the point but if it is just Html.Raw(value) you were hoping to use when finding this question on Google (as I was) according to the source code of System.Web.Mvc.dll all Html.Raw does is:

public IHtmlString Raw(string calue)
{
    return new HtmlString(value);
}

So I've just used @(new HtmlString(value)) in my helper which works nicely.

Just replace

 @Html.Raw(extra)

with

@(new HtmlString(extra))

I think I know what's causing the issue...

The definition of the Html property getter is:

public static HtmlHelper Html {
    get { 
        WebPage currentWebPage = CurrentPage as WebPage;
        if (currentWebPage == null) {
            return null;
        } 
        return currentWebPage.Html;
    } 
} 

Setting a breakpoint in my helper method shows that CurrentPage is not in fact an instance of WebPage, hence the null value.

Here is the type hierarchy of CurrentPage (my class names doctored slightly):

ASP._Page_Views_mycontroller_View_cshtml
  My.Site.MyWebViewPage`1
    System.Web.Mvc.WebViewPage`1
      System.Web.Mvc.WebViewPage
        System.Web.WebPages.WebPageBase
          System.Web.WebPages.WebPageRenderingBase
            System.Web.WebPages.WebPageExecutingBase
              System.Object

Note that the base class of my view has been specified in Web.config:

<system.web.webPages.razor>
  <pages pageBaseType="My.Site.MyWebViewPage">
    ...

Which is defined both in generic and non-generic form:

public abstract class MyWebViewPage : WebViewPage { ... }
public abstract class MyWebViewPage<TModel> : WebViewPage<TModel> { ... }

So, if this problem does not occur for others, perhaps they're not using a custom pageBaseType.

Note too that I've placed the @helper declaration in App_Code\Helpers.cshtml in the hope of making it globally accessible.

Am I doing something wrong, or is this a bug?

EDIT Thanks Darin for pointing out this as a known issue. Still, why isn't the Html property redefined as:

public static HtmlHelper Html {
    get { 
        WebPage currentWebPage = CurrentPage as WebPage;
        if (currentWebPage != null) {
            return currentWebPage.Html;
        } 
        WebViewPage currentWebViewPage = CurrentPage as WebViewPage;
        if (currentWebViewPage != null) {
            return currentWebViewPage.Html;
        } 
        return null;
    } 
} 
scarmuega

I had the same issue and this line of code did the trick. It´s not a solution for using HtmlHelper, it's just a way of writing RAW html in a declarative razor helper.

@{CurrentPage.WriteLiteral("html code");}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!