Custom HtmlHelper extension method usage problem in asp.net mvc razor - asp.net-mvc

I have converted my classic asp.net mvc views to razor. In a view there is a usage of an extension method (it has overloads) of HtmlHelper:
#Html.CustomAction<AccountController, LogOnModel>("displayText", x => x.Register())
And CustomAction signature is:
public static HtmlString CustomAction<TController, TModel>(this HtmlHelper<TModel> view, string displayText, Expression<Func<TController, object>> where TController : Controller
I have also enabled view compilation at build time (through .proj file). When I build the project I get these errors pointing to that line:
Argument 1: cannot convert from
'method group' to
'System.Web.WebPages.HelperResult'
The best overloaded method match for
'System.Web.WebPages.WebPageExecutingBase.Write(System.Web.WebPages.HelperResult)'
has some invalid arguments
What is the reason of these errors? How can I correct it?

The Razor parser sees < and thinks it's an HTML tag. Therefore, it only parses Html.CustomAction as the expression.
You need to wrap the call in parentheses to force it to treat the entire call as a single expression:
#(Html.CustomAction<AccountController, LogOnModel>("displayText", x => x.Register()))

Related

Razor - Inline helper with multiple tags

I can call a method which accepts an HTML/Razor block as a parameter like so:
#Html.SiteText(#<div>Some content</div>)
Calling the same method with multiple top level tags fails due to a parser error. I.E.
#Html.SiteText(#<div>Some content</div><div>Some more content</div>)
fails with:
Compiler Error Message: CS1026: ) expected
Is there any way to pass a Razor helper which has multiple top level tags?
Edit:
Here's the signature of the SiteText extension method:
public static IHtmlString SiteText(this HtmlHelper htmlHelper, Func<object, HelperResult> content)
The actual implementation of the method shouldn't matter, as this applies to how the method is invoked rather than what the implementation does with the inputs.
The simple way to look at it is that the Razor view engine is looking at each of those div tags as a single property.
Wrapping them in a text tag should resolve the issue:
#Html.SiteText(#<text><div>Some content</div><div>Some more content</div></text>)
Not tested but I suspect this will solve it:
#Html.SiteText(#<text>
<div>Some content</div>
<div>Some more content</div>
</text>)

ASP .NET MVC localization when dynamic variables are included?

Consider the following string if I need to localize it:
You need to write <b>#ViewBag.CharacterAmount</b> characters to be able to
hand-in this homework. You're still missing <b id="charactersRemaining">
#ViewBag.CharacterAmount</b> characters to reach this limit.
What would be the best approach? Using string.Format is a bit complex, since ASP .NET MVC escapes HTML code, and besides, I'd rather be free of HTML code in my resource files. However, I still need to be able to refer to those values inside the b tags from JavaScript.
Any ideas? What is your approach to this when you do localization?
You could write a custom helper:
public static class ResourcesExtensions
{
public static IHtmlString Resource(this HtmlHelper htmlHelper, string message, params object[] args)
{
var parameters = args.Select(x => htmlHelper.Encode(x)).ToArray();
return new HtmlString(string.Format(message, parameters));
}
}
As you can see the HTML helper encodes only the values. We have full control over the rest of the message because it is in the resources file and we suppose that it is valid HTML, so no problem with XSS.
and then have a resources file to your project which will contain for example the following key:
MyMessage = You need to write <b>{0}</b> characters to be able to hand-in this homework. You're still missing <b id="charactersRemaining">{1}</b> characters to reach this limit.
and then don't forget to mark this resources file with the PublicResXFileCodeGenerator custom tool so that Visual Studio generates a public class that will allow you to access the properties in the view.
and finally in the view:
#Html.Resource(Resources.MyMessage, (int)ViewBag.CharacterAmount, (int)ViewBag.CharacterAmount)
The reason you need to cast is because extension method cannot dispatch dynamic arguments. But obviously that's not a problem at all because you shouldn't be using ViewBag/ViewData but you should be using view models and strongly typed view models so in your real code you will have:
#Html.Resource(Resources.MyMessage, Model.CharacterAmount, Model.CharacterAmount)
One downside with this approach is that we have moved some markup in the resources file which unfortunately might render the views a little less understandable and when we need to modify it, we should do this in all localized versions.
Another approach of course consists into putting in your resources file every distinct part of this markup and then:
#Resources.YouNeedToWrite <b>ViewBag.CharacterAmount</b> #Resources.StillMissing
<b id="charactersRemaining">ViewBag.CharacterAmount</b> #(Resources.ToReachLimit).

ASP.NET MVC: Custom Html Helpers in Razor

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

Rendering partial views with a model and htmlhelper

ASP .NET MVC 1
I'd like to show a partial view base on a model, and I'd like that to have a fairly short command for that. So I saw a way of using both a HtmlHelper and a controller (And I'd use another controller for that, not the controller currently used).
But somehow it still gives an error, though I think the method starts to look as it should.
So what am I doing wrong? (If I call the method directly in the ASPX-page, it succeeds. But it should be possible to use a HtmlHelper for that).
public static void RenderPartialView(this HtmlHelper html, string action, string controller, object model)
{
var context = html.ViewContext;
RouteData rd = new RouteData(context.RouteData.Route, context.RouteData.RouteHandler);
rd.Values.Add("controller", controller);
rd.Values.Add("action", action);
rd.Values.Add("model", model);
IHttpHandler handler = new MvcHandler(new RequestContext(context.HttpContext, rd));
handler.ProcessRequest(System.Web.HttpContext.Current);
}
Part in the ASCX-page:
<% Html.RenderPartialView("Show", "Intro", Model.Intro); %>
Error given:
'System.Web.Mvc.HtmlHelper' does not contain a definition for 'RenderPartialView' and no extension method 'RenderPartialView' accepting a first argument of type 'System.Web.Mvc.HtmlHelper' could be found (are you missing a using directive or an assembly reference?)
Why don't you use Html.RenderPartial ? It's the correct way to render a partial view. No need to make another request.
<% Html.RenderPartial("Show", Model.Intro); %>
Your call does not succeed beacause when you use an extension method in a "non static" way (i.e, as if the method belongs to an instance), you must omit the first parameter. The correct call would be
<% Html.RenderPartialView("Show", "Intro", Model.Intro); %>
Hope it helps
Cédric
Add
<add namespace="Namespace-Of-RenderPartialView-Class"/>
to your web.config file.
With extension methods, you don't need to include the first argument (the "this HtmlHelper html"). That is handled by the compiler when using extension methods. It is inferred based on the object you're calling the method on.

How does ASP.Net MVC ActionLink Work?

I am trying to write my own LightWeight MVC for .Net 2.0 using NHaml as the view engine.
In ASP.Net 3.5 MVC the View file we used to specify the link by the code snippet.
Html.ActionLink("Add Product","Add");
In MVC binary there is no function to match this call.
I Only found:
(In class System.Web.Mvc.Html.LinkExtensions )
public static string ActionLink(this System.Web.Mvc.HtmlHelper htmlHelper,
string linkText, string actionName)
There are more similar static classes like FormExtensions, InputExtensions etc.
How does ASP.Net MVC handle it? Does it generates dynamic code for Html.ActionLink?
The ActionLink method is an extension method (hence the this before the type of the first parameter). This means you can use this method as an instance method on all HtmlHelper instances, even though it is not defined on HtmlHelper.
Html is a property on the View of type HtmlHelper. This means you can use the ActionLink extensionmethod on it.
The ActionLink method itself does nothing more than generate a link string (with regards to its arguments) and return that string.
Have you checked out the code on Codeplex? The MVC Framwork is open source, so you can dig around as much as you need to.

Resources