Has anyone written one? I want it to behave like a link but look like a button. A form with a single button wont do it has I don't want any POST.
The easiest way to do it is to have a small form
tag with method="get"
, in which you place a submit button:
<form method="get" action="/myController/myAction/">
<input type="submit" value="button text goes here" />
</form>
You can of course write a very simple extension method that takes the button text and a RouteValueDictionary
(or an anonymous type with the routevalues) and builds the form so you won't have to re-do it everywhere.
EDIT: In response to cdmckay's answer, here's an alternative code that uses the TagBuilder
class instead of a regular StringBuilder
to build the form, mostly for clarity:
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Web.Routing;
namespace MvcApplication1
{
public static class HtmlExtensions
{
public static string ActionButton(this HtmlHelper helper, string value,
string action, string controller, object routeValues)
{
var a = (new UrlHelper(helper.ViewContext.RequestContext))
.Action(action, controller, routeValues);
var form = new TagBuilder("form");
form.Attributes.Add("method", "get");
form.Attributes.Add("action", a);
var input = new TagBuilder("input");
input.Attributes.Add("type", "submit");
input.Attributes.Add("value", value);
form.InnerHtml = input.ToString(TagRenderMode.SelfClosing);
return form.ToString(TagRenderMode.Normal);
}
}
}
Also, as opposed to cdmckay's code, this one will actually compile ;) I am aware that there might be quite a lot of overhead in this code, but I am expecting that you won't need to run it a lot of times on each page. In case you do, there is probably a bunch of optimizations that you could do.
@MvcHtmlString.Create(Html.ActionButton(value, action, controller, routeValues))
–
Baudoin @Html.ActionButton("Create New", "Create", "ASSETs", null)
if you return an MvcHtmlString
and just change the return statement to return new MvcHtmlString(form.ToString());
@C1phr –
Alessandro If you want it to behave like a link but "look" like a button just use the ActionLink with a CSS Class.
<%: Html.ActionLink("Back", "Index", null, new { @class = "link-button" })%>
Here is the CSS for a Button that I am using.
.link-button {
-moz-border-radius:0.333em 0.333em 0.333em 0.333em;
-moz-box-shadow:0 1px 4px rgba(0, 0, 0, 0.4);
background:-moz-linear-gradient(center top , white, #306AB5 4%, #274976) repeat scroll 0 0 transparent;
border-color:#306AB5 #2B5892 #274771;
border-style:solid;
border-width:1px;
color:white;
cursor:pointer;
display:inline-block;
font-size:1.167em;
font-weight:bold;
line-height:1.429em;
padding:0.286em 1em 0.357em;
text-shadow:0 1px 2px rgba(0, 0, 0, 0.4);
}
.link-button {
color: white;
border-color: #a1a7ae #909498 #6b7076;
background: #9fa7b0 url(../images/old-browsers-bg/button-element-grey-bg.png) repeat-x top;
background: -moz-linear-gradient(
top,
white,
#c5cbce 5%,
#9fa7b0
);
background: -webkit-gradient(
linear,
left top, left bottom,
from(white),
to(#9fa7b0),
color-stop(0.05, #c5cbce)
);
-moz-text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
-webkit-text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.6);
-moz-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
-webkit-box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2);
}
.link-button:hover {
border-color: #a1a7b0 #939798 #6e7275;
background: #b1b5ba url(../images/old-browsers-bg/button-element-grey-hover-bg.png) repeat-x top;
background: -moz-linear-gradient(
top,
white,
#d6dadc 4%,
#b1b5ba
);
background: -webkit-gradient(
linear,
left top, left bottom,
from(white),
to(#b1b5ba),
color-stop(0.03, #d6dadc)
);
}
.link-button:active {
border-color: #666666 #ffffff #ffffff #979898;
background: #dddddd url(../images/old-browsers-bg/button-element-grey-active-bg.png) repeat-x top;
background: -moz-linear-gradient(
top,
#f1f1f1,
#dddddd
);
background: -webkit-gradient(
linear,
left top, left bottom,
from(#f1f1f1),
to(#dddddd)
);
}
Two versions to make an extension for...
<button
onclick="javascript:window.location=('@Url.Action("Review", "Order", null)')"
>Review Order</button>
Unobtrusive version:
<button data-action="@Url.Action("Review", "Order", null)">Review Order</button>
$(document).on('click', "[data-action]",
function(e) { window.location = $(this).attr('data-action'); }
);
If you have users which do not have javascript turned on, then form tag is the way to go. Although, this make the situation difficult if your link is already within a form. However, you could change the action and method to GET.
Code for Tomas' answer:
public static class HtmlExtensions
{
public static string ActionButton(this HtmlHelper helper, string value,
string action, string controller, object routeValues)
{
UrlHelper urlHelper = new UrlHelper(helper.ViewContext);
var action = urlHelper.Action(action, controller, routeValues);
var html = new StringBuilder();
html.AppendFormat("<form method='get' action'{0}'>", action).AppendLine()
.AppendFormat(" <input type='submit' value='{0}' />", value).AppendLine()
.AppendFormat("</form>").AppendLine();
return html.ToString();
}
}
I modified Tomas Lyckens code to return a MvcHtmlString instead of just a string. This makes sure the output is HTML, not escaped as text. I also xml-documented it nicely. Thanks to Tomas who did all the real work.
/// <summary>
/// Returns an HTML submit button (enclosed in its own form) that contains the virtual path of the specified action.
/// </summary>
/// <param name="helper">The HTML helper instance that this method extends.</param>
/// <param name="buttonText">The visible text of the button.</param>
/// <param name="action">The name of the action.</param>
/// <param name="controller">The name of the controller.</param>
/// <param name="routeValues">An object that contains the parameters for a route. The parameters are retrieved through reflection by examining the properties of the object. The object is typically created by using object initializer syntax.</param>
/// <returns></returns>
public static MvcHtmlString ActionButton(this HtmlHelper helper, string buttonText, string action, string controller, object routeValues)
{
string a = (new UrlHelper(helper.ViewContext.RequestContext)).Action(action, controller, routeValues);
var form = new TagBuilder("form");
form.Attributes.Add("method", "get");
form.Attributes.Add("action", a);
var input = new TagBuilder("input");
input.Attributes.Add("type", "submit");
input.Attributes.Add("value", buttonText);
form.InnerHtml = input.ToString(TagRenderMode.SelfClosing);
return MvcHtmlString.Create(form.ToString(TagRenderMode.Normal));
}
Here is a VB.NET version with one extra thing, the controller and routes parameters are optional so it can be used without repeating the controller name if it's the same as the current/default controller for the page.
Public Module MvcExtensions
''' <summary>
''' Returns an HTML submit button (enclosed in its own form) that contains the virtual path of the specified action.
''' </summary>
''' <param name="helper">The HTML helper instance that this method extends.</param>
''' <param name="text">Text displayed in the button.</param>
''' <param name="action">Action name.</param>
''' <param name="controller">Optional controller name, using current when null.</param>
''' <param name="routeValues">
''' An object that contains the parameters for a route. The parameters are retrieved
''' through reflection by examining the properties of the object.
''' The object is typically created by using object initializer syntax.
''' </param>
''' <returns>
''' HTML output.
''' </returns>
<Extension()> _
Public Function ActionButton(helper As HtmlHelper, text As String, action As String,
Optional controller As String = Nothing, Optional routeValues As Object = Nothing) As MvcHtmlString
' Validate parameters
If String.IsNullOrEmpty(text) Then Throw New ArgumentNullException("text")
' Get post back URL for action
Dim actionUrl As String = New UrlHelper(helper.ViewContext.RequestContext).Action(action, controller, routeValues)
' Build form tag with action URL
Dim form = New TagBuilder("form")
form.Attributes.Add("method", "get")
form.Attributes.Add("action", actionUrl)
' Add button
Dim input = New TagBuilder("input")
input.Attributes.Add("type", "submit")
input.Attributes.Add("value", text)
form.InnerHtml = input.ToString(TagRenderMode.SelfClosing)
' Return result as HTML
Return MvcHtmlString.Create(form.ToString(TagRenderMode.Normal))
End Function
End Module
Then it can be invoked like the other other MVC controls, with minimum code on the page.
This should really have gone into the core MVC framework from the start; it seems such an obvious requirement. I think buttons are much more intuitive for the user when performing actions which create or change things, rather than links. Links should just navigate to related information (not change anything). If i had a grid I would use ActionLink for any data navigation (e.g. click a product name to goto product page) but only ActionButton for real "actions" like edit and delete.
If you want a button that functions like a link this should work:
<input type="button" value="Button Text"
onclick="@("location.href='https://mcmap.net/q/957305/-mvc-html-actionbutton/2460971'")" />
If you want the button to use a controller action only a minor change is needed:
<input type="button" value="Button Text"
onclick="@("location.href='" + Url.Action("ActionName", "ControllerName") + "'")" />
© 2022 - 2024 — McMap. All rights reserved.