81

this is weird. I see references out there for @Html.Button() but when I type that Intellisense doesn't find such a helper... there's dropdownlist, hidden, editors, et cetera, but no button!

what's up with that?

abatishchev
  • 95,331
  • 80
  • 293
  • 426
ekkis
  • 8,904
  • 12
  • 48
  • 93
  • Where do you see such references? – SLaks May 10 '11 at 20:03
  • 1
    I understand the desire for consistency but there's really no need for one (nothing to encode or protect against) so all it would be is a long-hand way to write a single html tag: '@Html.Button("myButton", new { @class = "myClass"})' vs '' – Dr. Ogden Wernstrom Jan 08 '15 at 15:57
  • 4
    @Russel - I would disagree - I have created extension methods on MvcHtmlString to disable, make readonly, and hide DOM elements. Because there is no HTML helper for buttons I cannot use my extension methods. To say 'there is no need' is a bit short-sighted and possibly naive. – barrypicker Aug 26 '15 at 17:36

5 Answers5

69
public static class HtmlButtonExtension 
{

  public static MvcHtmlString Button(this HtmlHelper helper, 
                                     string innerHtml, 
                                     object htmlAttributes) 
  { 
    return Button(helper, innerHtml,
                  HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)
    ); 
  }

  public static MvcHtmlString Button(this HtmlHelper helper, 
                                     string innerHtml,
                                     IDictionary<string, object> htmlAttributes)
  {
      var builder = new TagBuilder("button");
      builder.InnerHtml = innerHtml;
      builder.MergeAttributes(htmlAttributes);
      return MvcHtmlString.Create(builder.ToString());
  }
}
jessegavin
  • 70,879
  • 26
  • 136
  • 164
  • 1
    Yeah... they must have had some really good reason otherwise it would be a huge mistake. – Andrei Rînea May 17 '11 at 08:17
  • Thanks, I was on a new project and surprised to see no html.button – Tom Stickel Feb 24 '12 at 23:37
  • 7
    If you would like to use the `@Html.Button("ButtonText", new {Name = "name", Value = "value" })` format, add this class: `public static MvcHtmlString Button(this HtmlHelper helper, string text, object htmlAttributes) { return Button(helper, text, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes)); }` – Mason240 Jan 21 '13 at 22:28
  • 1
    Good call @Mason240. I updated my answer with an overload as you suggested. – jessegavin Jan 22 '13 at 01:04
  • whoa...how have I never seen `HtmlHelper.AnonymousObjectToHtmlAttributes` before? – drzaus Apr 02 '14 at 19:12
  • I get the following error when using the above code: `'HtmlHelper' does not contain a definition for 'Button' and no extension method 'Button' accepting a first argument of type 'HtmlHelper' could be found (are you missing a using directive or an assembly reference?) ` I put the extension in the `namespace System.Web.Mvc.Html` – Legends Apr 29 '16 at 12:06
9

To expand on the accepted answer, so you can bind a submit button to a model property but have different text:

@Html.ButtonFor(m => m.Action, Model.LabelForCurrentAction(), new { @class = "btn btn-primary btn-large", type = "submit" })

Using the following slightly modified Button helper class:

/// <summary>
/// Via https://stackoverflow.com/questions/5955571/theres-no-html-button
/// </summary>
public static class HtmlButtonExtension
{

    public static MvcHtmlString Button(this HtmlHelper helper, object innerHtml, object htmlAttributes)
    {
        return helper.Button(innerHtml, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
    }

    public static MvcHtmlString Button(this HtmlHelper helper, object innerHtml, IDictionary<string, object> htmlAttributes)
    {
        var builder = new TagBuilder("button") { InnerHtml = innerHtml.ToString() };
        builder.MergeAttributes(htmlAttributes);
        return MvcHtmlString.Create(builder.ToString());
    }

    public static MvcHtmlString ButtonFor<TModel, TField>(this HtmlHelper<TModel> html,
                                                        Expression<Func<TModel, TField>> property,
                                                        object innerHtml,
                                                        object htmlAttributes)
    {
        // lazily based on TextAreaFor

        var attrs = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);

        var name = ExpressionHelper.GetExpressionText(property);
        var metadata = ModelMetadata.FromLambdaExpression(property, html.ViewData);

        string fullName = html.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);

        ModelState modelState;
        if (html.ViewData.ModelState.TryGetValue(fullName, out modelState) && modelState.Errors.Count > 0)
        {
            if( !attrs.ContainsKey("class") ) attrs["class"] = string.Empty;
            attrs["class"] += " " + HtmlHelper.ValidationInputCssClassName;
            attrs["class"] = attrs["class"].ToString().Trim();
        }
        var validation = html.GetUnobtrusiveValidationAttributes(name, metadata);
        if(null != validation) foreach(var v in validation) attrs.Add(v.Key, v.Value);

        string value;
        if (modelState != null && modelState.Value != null)
        {
            value = modelState.Value.AttemptedValue;
        }
        else if (metadata.Model != null)
        {
            value = metadata.Model.ToString();
        }
        else
        {
            value = String.Empty;
        }

        attrs["name"] = name;
        attrs["Value"] = value;
        return html.Button(innerHtml, attrs);
    }
}
Community
  • 1
  • 1
drzaus
  • 23,063
  • 16
  • 132
  • 194
9

There is no button helper as of mvc preview 3 (not mvc3)

it was mentioned a bunch in the past for example: http://blog.wekeroad.com/blog/aspnet-mvc-preview-using-the-mvc-ui-helpers/ however rolling your own is trivial - I have a basis for it somewhere around here I'll have to dig it up, but essentially just create a new Html.ButtonFor and copy the source code from Html.LabelFor and change the output to create an input type="button" tag.

Adam Tuliper
  • 29,829
  • 4
  • 51
  • 70
8

MVC5 , Bootstrap ver 3.2.0

@Html.ActionLink
(
    linkText: " Remove", 
    actionName: "Index", 
    routeValues: null, // or to pass some value -> routeValues: new { id = 1 },
    htmlAttributes: new { @class = "btn btn-success btn-sm glyphicon glyphicon-remove" }
)

This will generate a link that looks like a button.

Piotr Knut
  • 350
  • 3
  • 8
  • 2
    what's "Bootstrap 3"? – ekkis Oct 10 '14 at 23:48
  • 1
    I'm sorry, I typed the wrong word. Of course, I mean the Bootstrap. Thank ekkis. [link](http://getbootstrap.com/) – Piotr Knut Oct 12 '14 at 14:40
  • 2
    This does not create a button, instead a link that looks like a button. – SMUsamaShah Apr 19 '15 at 14:51
  • 2
    For me this fails to compile if the opening parenthesis is not on the same line as ActionLink. – GaTechThomas Nov 04 '16 at 23:19
  • GaTechThomas. Yes it's true. Everything should be in one line. I reformatted it this way that was easier to read. It is best to create your own button (or something else), but if you do not have time to do it or are not worried about its appearance that it is a quick solution. – Piotr Knut Nov 20 '16 at 00:19
1

Razor does not appear to have a "Button" HTML helper. You're likely finding references to a custom-built HTML helper extension.

See here: http://www.asp.net/mvc/tutorials/creating-custom-html-helpers-cs

Ben Finkel
  • 4,693
  • 6
  • 32
  • 48
  • 1
    doesn't that seem bizarre? especially if it's trivial, why not include it for completeness?? – ekkis May 11 '11 at 04:36
  • I've read through that doc but I can't make it work. I've placed my helper in a file called HtmlHelpers.cs in the root of the project and it contains a static class HtmlHelpers with a public static method that returns a string whose only parameter is "this HtmlHelper helper". everything compiles and the .Button should appear in Intellisense but it doesn't... what else am I supposed to do? – ekkis May 11 '11 at 05:11
  • 1
    did you include the import statement for your extension namespace at the top of your view? thats the usual issue here. you may want to make sure your views compile (right click on project, select unload. right click to edit the file - set MvcBuildViews to true. save and close, and right click and reload project and compile again. – Adam Tuliper May 11 '11 at 05:30
  • I've moved the file into the Views/Shared folder and renamed it Helpers (the class too) but no dice... I'm using "namespace Website" which is my app's namespace and (I think) should thus be included in every view but I still can't get it to work. grr... what could it be? – ekkis May 11 '11 at 05:40
  • @Adam, I tried unloading the project... unfortunately, I've got some Visual Studio "explorer" extension that blew up "The operation could not be completed. Not implemented." and I had to kill VS. I'm not using a separate namespace... do I need to do that? – ekkis May 11 '11 at 05:43
  • @Adam, now I'm messed up. I can't reload the project, when I try it complains "A project with that name is already opened in the solution." thoughts? – ekkis May 11 '11 at 05:53
  • I disabled the Productivity Power Tools and restarted VS but I still couldn't get it to load the project. I found an SO article that suggested deleting the .suo file which fixed it (ooph). – ekkis May 11 '11 at 06:01
  • 1
    create a folder called extensions. add the htmlextensions.cs class there (making sure the namespace is correct) and then in your view use: @using Yourapp.Extensions also enable the MvcBuildViews using notepad if you are having an issue. – Adam Tuliper May 11 '11 at 06:15
  • MvcBuildViews is cool. though once I try to compile the project I got the error: "Could not load type 'System.Data.Entity.Design.AspNet.EntityDesignerBuildProvider'" from my web.config. I found an [http://stackoverflow.com/questions/2762256/mvcbuildviews-true-with-entity-framework-in-asp-net-mvc-2](SO article) that indicated I needed to include an assembly reference ``, however that just leads to another error... – ekkis May 11 '11 at 06:20
  • ..."It is an error to use a section registered as allowDefinition='MachineToApplication' beyond application level", which I have yet to figure out. there are suggestions out there that having two Web.Config files (maybe having one in a subfolder) will cause this but that's not my problem. there's also talk of having to configure your app for IIS, though I'm deploying to IIS Express without a problem so I'm not sure that's the issue... – ekkis May 11 '11 at 06:22
  • 1
    Ekkis, Not sure what you've done to your project but you need to do the following to create an HtmlHelperExtension: 1) Create a static class (I call mine HtmlHelperExtensions) 2) Within that class make public methods, annotated as , with the proper signature. 3) Return MvcHtmlString.Create(). See this post for a useful and functional example: http://stackoverflow.com/questions/4896439/action-image-mvc3-razor – Ben Finkel May 11 '11 at 15:05
  • ok, I got it all to work. I found that under the obj\ directory there were several other copies of the Web.Config so I blew away the whole directory and recompiled (I did have to add the assembly reference indicated above). Everything worked great! – ekkis May 11 '11 at 18:24
  • In summary, it doesn't matter what namespace the your class that contains your HtmlHelper methods is in. What matters is that the view has access to that namespace. I (wrongfully) assumed views would have access to my application's namespace. I decided in the end to use Website.HtmlHelpers, but could have just as easily used just Website. Thanks everyone for the help! – ekkis May 11 '11 at 18:26
  • @Ben Finkel, excellent article. now that I've got the mechanism to work I have a template for building these things. thanks so much. – ekkis May 11 '11 at 18:27