ASP.NET MVC: Custom Html Helpers in Razor

asp.net Mvcasp.net Mvc-3Razor

asp.net Mvc Problem Overview


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.

asp.net Mvc Solutions


Solution 1 - asp.net Mvc

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()) %>

Solution 2 - asp.net Mvc

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...

Solution 3 - asp.net Mvc

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

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
QuestionawrigleyView Question on Stackoverflow
Solution 1 - asp.net MvcGvSView Answer on Stackoverflow
Solution 2 - asp.net MvcawrigleyView Answer on Stackoverflow
Solution 3 - asp.net MvcAtanas KorchevView Answer on Stackoverflow