63

I am having difficulty with Html Helpers when used with Razor. Said helpers worked fine in MVC 2 with the web form view engine. But not in razor. The error I get at runtime is:

Compiler Error Message: CS1502: The best overloaded method match for 'System.Web.WebPages.WebPageExecutingBase.Write(System.Web.WebPages.HelperResult)' has some invalid arguments

Source Error:


Line 1:  @using Wingspan.Web.Mvc;
Line 2:  @Html.IncrementalMenu(MenuBlock.Site)

Expanding the Show Detailed Compiler Output reveals:

d:\...\Views\Shared\MenuTop.cshtml(2,1): error CS1502: The best overloaded method match for 'System.Web.WebPages.WebPageExecutingBase.Write(System.Web.WebPages.HelperResult)' has some invalid arguments
d:\...\Views\Shared\MenuTop.cshtml(2,7): error CS1503: Argument 1: cannot convert from 'void' to 'System.Web.WebPages.HelperResult'

That indicates to me that razor doesn't like my helper, IncrementalMenu, returning void (which works fine in MVC 2 web form engine views).

I get no errors at Compile time, although the line of code (@Html.IncrementalMenu(...)) is red underlined with the following message:

Cannot implicitly convert type 'void' to 'object'

IncrementalMenu is in the Wingspan.Web.Mvc namespace. It's signature is as follows:

public static void IncrementalMenu(this HtmlHelper html, MenuBlock menuBlock)
{
    // Uses an HtmlTextWriter to render a menu from the sitemap
}

I'm blowed if I know what is wrong...

PS:

The MenuBlock parameter is just an enum that identifies how the menu should render. Don't fixate on this as that is fine.

awrigley
  • 13,121
  • 9
  • 78
  • 125

3 Answers3

85

You can call your helper like this:

@{ Html.IncrementalMenu(MenuBlock.Site); }

WebForms syntax

<% Html.IncrementalMenu(MenuBlock.Site); %>

You just call your method, and the return value (if there is any) is ignored.

Code like this expects a return value, and writes the return value to the html stream:

@Html.YourHelper()

Webforms syntax:

<%: Html.YourHelper() %>

The same, if result value != IHtmlString:

<%= Server.HtmlEncode(Html.YourHelper()) %>
GvS
  • 50,659
  • 16
  • 96
  • 138
49

Addendum:

You can get the same, or similar, error with @Html.RenderPartial. In this case it is due to the fact that RenderPartial renders directly to the Response, so is not a string and needs to be coded inside a "Razor code block":

@{
   Html.RenderPartial(...);
}

I suspect that is one of the reasons that Microsoft have included in ASP.NET MVC the new Html.Partial. As Html.Partial does return a string, it is OK to write:

@Html.Partial

Which looks a lot better. Given that one of Razor's declared objectives is to be easy on the eye, this is quite likely true.

It also kind of makes me, at least, feel more comfortable. I know what returning a string is, I do it all the time. But "returning to the Response" requires a few more brain cycles every time I think it.

And it fits with the old adage that finally Microsoft get their products right in version 3. EG, Access 97.

Which is a depressing simile. Cos they screwed things up in version 4, ie, Access 2000...

awrigley
  • 13,121
  • 9
  • 78
  • 125
  • 4
    Oh, I guess you just saved me half an hour. Thanks. – Dan Abramov May 28 '11 at 23:55
  • RenderPartial doesn't actually render directly to the response; if it did it would mess up Razor Layouts, where first the page itself is rendered to a buffer, then that's used to render its Layout, then that's used on its parent Layout up the chain until there are no more parent Layouts. Then finally the Response is actually written out. If RenderPartial() rendered to the Response the moment it was executed in a page, it would be the first thing in the Response - instead it just appends a string at that point in the buffered output, meaning it has the same effects as @Html.Partial. – Chris Moschini Jun 13 '13 at 15:10
23

Your HTML helper should return MvcHtmlString which represents the html in order to work properly with Razor (and other view engines that are not the WebFormsViewEngine)

public static MvcHtmlString Label(this HtmlHelper html, string expression)
{
    return MvcHtmlString.Create("<label>" + expression + "</label>");
}
Atanas Korchev
  • 30,192
  • 8
  • 55
  • 90
  • Thanks so much, this took me forever to figure out because I come from the ASPX view engine... – Ryan Oct 01 '11 at 23:28