How can I use the button tag with ASP.NET? How can I use the button tag with ASP.NET? asp.net asp.net

How can I use the button tag with ASP.NET?


Although you say that using the [button runat="server"] is not a good enough solution it is important to mention it - a lot of .NET programmers are afraid of using the "native" HTML tags...

Use:

<button id="btnSubmit" runat="server" class="myButton"     onserverclick="btnSubmit_Click">Hello</button>

This usually works perfectly fine and everybody is happy in my team.


This is an old question, but for those of us unlucky enough still having to maintain ASP.NET Web Forms applications, I went through this myself while trying to include Bootstrap glyphs inside of built-in button controls.

As per Bootstrap documentation, the desired markup is as follows:

<button class="btn btn-default">    <span class="glyphicon glyphicon-search" aria-hidden="true"></span>    Search</button>

I needed this markup to be rendered by a server control, so I set out to find options.

Button

This would be the first logical step, but —as this question explains— Button renders an <input> element instead of <button>, so adding inner HTML is not possible.

LinkButton (credit to Tsvetomir Tsonev's answer)

Source

<asp:LinkButton runat="server" ID="uxSearch" CssClass="btn btn-default">    <span class="glyphicon glyphicon-search" aria-hidden="true"></span>    Search</asp:LinkButton>

Output

<a id="uxSearch" class="btn btn-default" href="javascript:__doPostBack(&#39;uxSearch&#39;,&#39;&#39;)">    <span class="glyphicon glyphicon-search" aria-hidden="true"></span>    Search</a>

Pros

  • Looks OK
  • Command event; CommandName and CommandArgument properties

Cons

  • Renders <a> instead of <button>
  • Renders and relies on obtrusive JavaScript

HtmlButton (credit to Philippe's answer)

Source

<button runat="server" id="uxSearch" class="btn btn-default">    <span class="glyphicon glyphicon-search" aria-hidden="true"></span>    Search</button>

Result

<button onclick="__doPostBack('uxSearch','')" id="uxSearch" class="btn btn-default">    <span class="glyphicon glyphicon-search" aria-hidden="true"></span>    Search</button>

Pros

  • Looks OK
  • Renders proper <button> element

Cons

  • No Command event; no CommandName or CommandArgument properties
  • Renders and relies on obtrusive JavaScript to handle its ServerClick event

At this point it is clear that none of the built-in controls seem suitable, so the next logical step is try and modify them to achieve the desired functionality.

Custom control (credit to Dan Herbert's answer)

NOTE: This is based on Dan's code, so all credit goes to him.

using System.Web.UI;using System.Web.UI.WebControls;namespace ModernControls{    [ParseChildren]    public class ModernButton : Button    {        public new string Text        {            get { return (string)ViewState["NewText"] ?? ""; }            set { ViewState["NewText"] = value; }        }        public string Value        {            get { return base.Text; }            set { base.Text = value; }        }        protected override HtmlTextWriterTag TagKey        {            get { return HtmlTextWriterTag.Button; }        }        protected override void AddParsedSubObject(object obj)        {            var literal = obj as LiteralControl;            if (literal == null) return;            Text = literal.Text;        }        protected override void RenderContents(HtmlTextWriter writer)        {            writer.Write(Text);        }    }}

I have stripped the class down to the bare minimum, and refactored it to achieve the same functionality with as little code as possible. I also added a couple of improvements. Namely:

  • Remove PersistChildren attribute (seems unnecessary)
  • Remove TagName override (seems unnecessary)
  • Remove HTML decoding from Text (base class already handles this)
  • Leave OnPreRender intact; override AddParsedSubObject instead (simpler)
  • Simplify RenderContents override
  • Add a Value property (see below)
  • Add a namespace (to include a sample of @ Register directive)
  • Add necessary using directives

The Value property simply accesses the old Text property. This is because the native Button control renders a value attribute anyway (with Text as its value). Since value is a valid attribute of the <button> element, I decided to include a property for it.

Source

<%@ Register TagPrefix="mc" Namespace="ModernControls" %><mc:ModernButton runat="server" ID="uxSearch" Value="Foo" CssClass="btn btn-default" >    <span class="glyphicon glyphicon-search" aria-hidden="true"></span>    Search</mc:ModernButton>

Output

<button type="submit" name="uxSearch" value="Foo" id="uxSearch" class="btn btn-default">    <span class="glyphicon glyphicon-search" aria-hidden="true"></span>    Search</button>

Pros

  • Looks OK
  • Renders a proper <button> element
  • Command event; CommandName and CommandArgument properties
  • Does not render or rely on obtrusive JavaScript

Cons

  • None (other than not being a built-in control)


I stumbled upon your question looking for the same exact thing. I ended up using Reflector to figure out how the ASP.NET Button control is actually rendered. It turns out to be really easy to change.

It really just comes down to overriding the TagName and TagKey properties of the Button class. After you've done that, you just need to make sure you render the contents of the button manually since the original Button class never had contents to render and the control will render a text-less button if you don't render the contents.

Update:

It's possible to make a few small modifications to the Button control through inheritance and still work fairly well. This solution eliminates the need to implement your own event handlers for OnCommand (although if you want to learn how to do that I can show you how that is handled). It also fixes the issue of submitting a value that has markup in it, except for IE probably. I'm still not sure how to fix IE's poor implementation of the Button tag though. That may just be a truly technical limitation that is impossible to work around...

[ParseChildren(false)][PersistChildren(true)]public class ModernButton : Button{    protected override string TagName    {        get { return "button"; }    }    protected override HtmlTextWriterTag TagKey    {        get { return HtmlTextWriterTag.Button; }    }    // Create a new implementation of the Text property which    // will be ignored by the parent class, giving us the freedom    // to use this property as we please.    public new string Text    {        get { return ViewState["NewText"] as string; }        set { ViewState["NewText"] = HttpUtility.HtmlDecode(value); }    }    protected override void OnPreRender(System.EventArgs e)    {        base.OnPreRender(e);        // I wasn't sure what the best way to handle 'Text' would        // be. Text is treated as another control which gets added        // to the end of the button's control collection in this         //implementation        LiteralControl lc = new LiteralControl(this.Text);        Controls.Add(lc);        // Add a value for base.Text for the parent class        // If the following line is omitted, the 'value'         // attribute will be blank upon rendering        base.Text = UniqueID;    }    protected override void RenderContents(HtmlTextWriter writer)    {        RenderChildren(writer);    }}

To use this control, you have a few options. One is to place controls directly into the ASP markup.

<uc:ModernButton runat="server"         ID="btnLogin"         OnClick="btnLogin_Click"         Text="Purplemonkeydishwasher">    <img src="../someUrl/img.gif" alt="img" />    <asp:Label ID="Label1" runat="server" Text="Login" /></uc:ModernButton>

You can also add the controls to the control collection of the button in your code-behind.

// This code probably won't work too well "as is"// since there is nothing being defined about these// controls, but you get the idea.btnLogin.Controls.Add(new Label());btnLogin.Controls.Add(new Table());

I don't know how well a combination of both options works as I haven't tested that.

The only downside to this control right now is that I don't think it will remember your controls across PostBacks. I haven't tested this so it may already work, but I doubt it does. You'll need to add some ViewState management code for sub-controls to be handled across PostBacks I think, however this probably isn't an issue for you. Adding ViewState support shouldn't be terribly hard to do, although if needed that can easily be added.