Click here to Skip to main content
Click here to Skip to main content
Go to top

ClickOnce Button Server Control

, 13 Apr 2004
Rate this:
Please Sign up or sign in to vote.
An article that shows how to do what everyone said you shouldn't or couldn't do with the ASP.NET button control.

Introduction

Back in the days of classic ASP, when we wanted to prevent the end user from repeatedly mashing the submit button, we would disable the button with JavaScript. But with ASP.NET, it's not as simple as that anymore. When you disable any form element client side, you essentially disable it server side as well since its name value pair will not be sent with the form post. The drawback from this is that the button_click event is now rendered useless. It will never be raised!

I have searched the ASP.NET forums over for an elegant solution for this problem and I have seen a couple of nice work arounds but nothing that gives the true effect of rendering the button disabled after being clicked. So I came up with my own. Enter the ClickOnceButton server control.

The server control behaves exactly like the intrinsic button control with the exception of two new properties: DisableAfterClick and DisabledText.

When DisableAfterClick is true, the button will be disabled client side after click. If DisabledText is set, the text of the button will be set to the DisabledText after the button is disabled. Useful if you want to get a li'l visual cue (such as changing the buttons text to 'Processing..').

The main work happens in the AddAttributesToRender event:

    Protected Overrides Sub AddAttributesToRender(ByVal writer As HtmlTextWriter)
        Dim strOnClick As String

        If IsNothing(MyBase.Page) Then
            MyBase.Page.VerifyRenderingInServerForm(Me)
        End If

        writer.AddAttribute(HtmlTextWriterAttribute.Type, "submit")
        writer.AddAttribute(HtmlTextWriterAttribute.Name, MyBase.UniqueID)
        writer.AddAttribute(HtmlTextWriterAttribute.Value, Me.Text)

        If Not IsNothing(MyBase.Page) And Me.CausesValidation_
                    And MyBase.Page.Validators.Count > 0 Then
            If Me.DisableAfterClick Then
                strOnClick = Me.GetClickOnceClientValidateEvent()
            Else
                strOnClick = Me.GetClientValidateEvent()
            End If

            If MyBase.Attributes.Count > 0 And Not _
                    IsNothing(MyBase.Attributes("onclick")) Then
              strOnClick = String.Concat(MyBase.Attributes("onclick"), strOnClick)
              MyBase.Attributes.Remove("onclick")
            End If

            writer.AddAttribute("language", "javascript")
            writer.AddAttribute(HtmlTextWriterAttribute.Onclick, strOnClick)
        ElseIf DisableAfterClick = True Then
            strOnClick = Me.GetOnceClickJavascript()

            If MyBase.Attributes.Count > 0 And Not _
                   IsNothing(MyBase.Attributes("onclick")) Then
              strOnClick = String.Concat(MyBase.Attributes("onclick"), strOnClick)
              MyBase.Attributes.Remove("onclick")
            End If

            writer.AddAttribute("language", "javascript")
            writer.AddAttribute(HtmlTextWriterAttribute.Onclick, strOnClick)
        End If

        MyBase.AddAttributesToRender(writer)
    End Sub

This creates the button programmatically as well as determines if the button causes validation and there are validators on the page or if the button just simply needs to be disabled. You'll notice it calls three distinct different properties. These are what makes the magic happen. They are as follows:

    Friend ReadOnly Property GetClientValidateEvent() As String
        Get
            Return "if (typeof(Page_ClientValidate) _
                      == 'function') Page_ClientValidate(); "
        End Get
    End Property

    Friend ReadOnly Property GetClickOnceClientValidateEvent() As String
        Get
            Return "if (typeof(Page_ClientValidate) == 'function') _
                    { if(Page_ClientValidate()) { " + _
                    GetOnceClickJavascript + " }} else { " + _
                    GetOnceClickJavascript + " }"
        End Get
    End Property

    Friend ReadOnly Property GetOnceClickJavascript() As String
        Get
            Return "document.getElementsByName('" + _
              Me.OnceClickBtnName + "').item(0).setAttribute('name'," + _
              "this.getAttribute('name')); this.disabled = true; " + _
              IIf(DisabledText = String.Empty, String.Empty, _
              "this.value = '" + DisabledText + "';") + _
              "this.form.submit();"
        End Get
    End Property

The property, GetClientValidateEvent(), returns the same JavaScript that the framework normally appends if your button control causes validation and there are validators on the page. This is to handle the scenario where you have the button on a page, set to not be disabled after click, and you have validators.

The property, GetClickOnceClientValidateEvent(), returns a slightly modified version of the above script. This handles the scenario when there are validators on the page and you want the button to be disabled after click. But you want to make sure it only gets disabled if the Page_ClientValidate() returns true or in the case of non IE browsers it'll just disable the button and submit.

The property, GetOnceClickJavascript(), returns the base set of JavaScript that disables handles disabling the button. It also makes sure the button gets sent over in the post so that the button click will be raised. It does this by exchanging the name attribute of a hidden field with the name of the button. The framework just needs to see the name/ID of the control sent over to raise its click event. The hidden field gets registered in the control's OnInit(EventArgs) event.

    Protected Overrides Sub OnInit(ByVal e As EventArgs)
        If Me.DisableAfterClick And Not Me.IsHiddenFieldRegistered Then
            MyBase.Page.RegisterHiddenField(Me.OnceClickBtnName, "")
        End If

        MyBase.OnInit(e)
    End Sub

    Private Function IsHiddenFieldRegistered() As Boolean
        For Each ctl As Control In MyBase.Page.Controls
            If TypeOf ctl Is HtmlControls.HtmlInputHidden Then
                If ctl.ID = Me.OnceClickBtnName Then
                    Return True
                End If
            End If
        Next

        Return False
    End Function

The default constant value of OnceClickBtnValue is __onceClickBtn. The IsHiddenFieldRegistered function just makes sure that the field is registered only once. This allows us to possibly have more than one button on the form that disables after click.

You may be wondering why I didn't just inherit from the System.Web.UI.WebControls.Button class. Well, the reason was the AddAttributesToRender event. I could have just hijacked that event and added the same code I have here and not have had to go through the entire process of creating the properties, functions, etc. The problem was I still needed to call MyBase.AddAttributesToRender which would then finally call the AddAttributesToRender in the System.Web.UI.WebControls.WebControl class. If I inherited from the Button class, I would end up with two onclicks and no control over the JavaScript outputted in the event of validation. This gives me complete control.

To use this in your own projects, simply add the clickoncebutton assembly to your toolbox and drag and drop it onto your webform and you're set! Enjoy!

Note: Tested on IE 6.0 and NS 7.0 with version 1.1 of the .NET framework.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Eric Plowe
Software Developer (Senior)
United States United States
28 year old developer currently working for Sapient, Inc.
 
Specialities include (but not limited to): C# And VB.Net, VB6 & 5, VBScript(Classic asp), JavaScript guru, web services, xml/xsl, C++ (Win32/COM/DCOM)
 
In my spare time I try to continue my pursuit of music (when i have time!).

Comments and Discussions

 
Questionenabling/ disabiling button PinmemberMember 28350564-Nov-08 23:18 
GeneralThanks Pinmemberhido262826-Aug-08 23:22 
GeneralThanks a lot works for aspnet1.1 Pinmemberyeya8a25-Jun-08 5:53 
GeneralCommandButton ClickOnceButton Server Control PinmemberS.S.Sivaprasad16-Jan-08 20:47 
GeneralThis button saved my *SS Pinmemberrobtathome@charter.net24-Apr-07 7:24 
GeneralRe: This button saved my *SS PinmemberEric Plowe3-Aug-07 5:03 
NewsA solution for asp.net 2.0 Pinmembersimplicityc26-Jan-07 12:24 
Questiondoesnt seem to trigger the ajax UpdateProgress control Pinmemberpdenomme4-Jan-07 4:52 
Questionsupport ValidationGroup functionality? Pinmemberlimkek17-Dec-06 15:42 
GeneralToo much trouble PinmemberRobotovich25-Oct-06 12:16 
GeneralClient scripts on .NET 2.0 Pinmemberjportelas3-Oct-06 5:48 
NewsOne more click once button article PinmemberTittle Joseph6-Aug-06 23:43 
GeneralJust what I needed PinmemberNathan Wheeler28-Mar-06 4:03 
GeneralRe: Just what I needed PinmemberEric Plowe28-Mar-06 5:09 
GeneralPrompt ClickOnce Button Server Control Pinmemberqherb13-Mar-06 8:52 
GeneralRe: Prompt ClickOnce Button Server Control PinmemberAnjum Rizwi1-May-06 19:32 
GeneralRe: Prompt ClickOnce Button Server Control Pinmemberqherb2-May-06 4:39 
GeneralRe: Prompt ClickOnce Button Server Control PinmemberAnjum Rizwi2-May-06 19:47 
Generalthank you Pinmemberearthdance30-Nov-05 7:42 
GeneralClick and Disable PinmemberYao Xi25-Oct-05 22:39 
Generalsubmit and onsubmit PinmemberKirk Quinbar19-Sep-05 6:18 
GeneralMy C# version - with some limitations Pinsusstrondborg18-Aug-05 13:59 
GeneralRe: My C# version - with some limitations Pinsusstrondbor18-Aug-05 18:24 
GeneralRe: My C# version - with some limitations Pinmembertrondborg19-Aug-05 6:28 
GeneralRe: My C# version - with some limitations Pinmemberhydernaqvi27-Feb-06 11:45 
GeneralRe: My C# version - with some limitations PinmemberRobotovich25-Oct-06 12:17 
Generalanother simple solution Pinmemberumar@programmer.net18-Aug-05 10:45 
GeneralRe: another simple solution PinmemberEric Plowe18-Aug-05 12:00 
GeneralRe: another simple solution Pinmemberboomtrek24-Jan-06 20:02 
GeneralMore simple solution Pinmembers_tarassov21-Jul-05 3:32 
GeneralRe: More simple solution PinsussAnonymous10-Aug-05 1:37 
GeneralRe: More simple solution Pinmemberhydernaqvi27-Feb-06 12:13 
GeneralRe: More simple solution Pinmemberamitbhavsar24-Aug-06 3:56 
GeneralRe: More simple solution Pinmembersheir26-Sep-06 9:44 
GeneralRe: More simple solution PinmemberJon Seigel14-Oct-08 3:45 
GeneralCoverted To Image Button PinmemberMartin McNally21-Mar-05 5:00 
GeneralRe: Coverted To Image Button PinmemberAshaman31-May-05 1:50 
GeneralRe: Coverted To Image Button PinmemberTittle Joseph6-Aug-06 23:56 
GeneralImageButton Pinmemberbcbond13-Jan-05 4:33 
GeneralBrilliant Pinmemberphilasmith12-Jan-05 13:44 
General__doPostBack Pinmemberangel.carrillo11-Jan-05 11:49 
QuestionLinkButton? Pinmemberpeter the cat6-Jan-05 3:48 
GeneralThank You * 1000 Pinmembersbguy29-Oct-04 12:00 
Generalcan't find assembly PinmemberTravis D Falls3-Oct-04 13:23 
GeneralRe: can't find assembly Pinmemberpeter the cat6-Jan-05 1:11 
GeneralRe: can't find assembly PinmemberAnjum Rizwi1-May-06 21:03 
GeneralMultiple buttons PinmemberMika21-Sep-04 21:28 
GeneralRe: Multiple buttons PinmemberMika28-Oct-04 22:04 
GeneralRe: Multiple buttons PinsitebuilderUwe Keim7-Feb-05 8:52 
General.NET Framework versions PinmemberMarceloBarros9-Sep-04 12:46 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140916.1 | Last Updated 14 Apr 2004
Article Copyright 2004 by Eric Plowe
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid