Have you ever been asked to create an ASP.NET page that
required you to protect users from themselves? All too often, this need is
seen when a page initiates a server side process that takes a significant
amount of time to complete. Often, users will fill out a mortgage application
form or perhaps an e-menu for a favorite pizza place, but in the time it takes
the server to respond, the user clicks on the submit button several times which
can result in too many loans and too much pizza.
Protecting users from themselves is an application usability
issue and there are many different ways you may choose to approach the problem.
One way to prevent users from posting the same request more than once is to
disable the button that initiates the request to the server. Disabling the
submit button will keep users from replicating data or creating parallel
requests for an identical action.
Unfortunately, there is a problem. In ASP.NET, you cannot
simply disable the button before you initiate the postback. Once a control is
disabled on the client, the ASP.NET runtime no longer recognizes the control in
the form collection. So how do you stop the submit button from being clicked
multiple times and keep the control exposed to the page code-behind?
The Process
The solution is quite simple. The first step is to run a
custom JavaScript function that executes before the ASP.NET __doPostBack
function. This function will initiate a command to disable the button, but it
will only do so after a short delay. Immediately after this command,
processing will continue to contact the server and complete the postback. The
key to this technique is that the wait time is long enough for the server to
get the information it needs, but a short enough not to confuse the user with
unexpected changes to the user interface.
The Code
The following example builds a basic ASP.NET page that
includes a single button on the page. When you click on the button, the page
will postback to the server, but before the request is made a JavaScript
function will run. The function will wait a fraction of a second and then
disable the button. This fulfills the criteria of waiting long enough to
contact the server, but disables the button fast enough that it is not
distracting to the user.
HTML
Starting with a new, blank ASP.NET WebForm, add a single
button control to the page:
Listing 1: ASP.NET markup for the Save button
<asp:button id="btnSave" text="Save"runat="server" />
Code Behind
On the server, you must add to the attributes of the server
control to hook into the client-side click event. When the user clicks on the
button, the custom JavaScript executes before the ASP.NET __doPostBack
function. The addition to the control is all done in the page load
event and is only necessary upon the first request of the page.
Listing 2: Add a client-side OnClick event to an
ASP.NET button
Private Sub Page_Load(ByVal sender As System.Object,_
ByVal e As System.EventArgs) HandlesMyBase.Load
If Not Me.Page.IsPostBack Then
Me.btnSave.Attributes.Add("onclick",_"btnSave_Click(this);")
End If
End Sub
In a real-world scenario when the user clicks on the button,
the page will make the appropriate calls to a business layer in order to
perform some kind of task (apply for a loan, order pizza, etc.). For the
sake of this example, the page will send a command to the server to tell it to
hesitate for five seconds by calling the Sleep method of the current Thread. This
will simulate a long server process. The following code will demonstrate this
technique in action.
Listing 3: Add a five second delay to simulate
real-world performance issues
Private Sub btnSave_Click(ByVal sender As _
System.Object, ByVal e As System.EventArgs)_
Handles btnSave.Click
System.Threading.Thread.Sleep(5000)
End Sub
JavaScript
The JavaScript for this solution is built to be generic and
reusable. In order to achieve the desired effect, the browser must wait before
disabling the button, but not hinder the server from processing.
The flow of execution for this process:
1. Run the custom function when the user clicks the button.
2. Initiate a delay on the client which will eventually
disable the calling button.
3. Return control to ASP.NET which will contact the server
for the postback.
Add the following JavaScript in the <head> section of
your page. The following is the implementation of this logic.
Listing 4: Generic JavaScript functions that
disables a control after a short interval
<script type="text/javascript">
function DisableControl(controlId)
{
document.getElementById(controlId).disabled =true;
}
function DisableControl_SetTimeout(controlId,interval)
{
setTimeout("DisableControl('" +controlId + "')",interval);
}
function btnSave_Click(control)
{
DisableControl_SetTimeout(control.id,100);
}
</script>
The DisableControl function
contains a reference to the calling control’s ID as its only argument. When
the function executes then the control is located on the page, its state is
updated and it becomes disabled.
JavaScript’s built-in setTimeout
function executes a function after waiting a specified amount of time. The DisableControl_Timeout function calls DisableControl
using the setTimeout feature.
The btnSave_Click function will
run when the user clicks on the button. Since you wired up this function in
your code-behind passing by this as an argument, you may access the control’s
properties directly.
You might be wondering why you would not just pass a
reference to the control to each of the functions, rather than the control ID. The
JavaScript setTimeout function requires that you pass
in a string of the function name and arguments along with delay interval. If
you pass in an object reference as an argument, the page will encounter a
JavaScript error.
Run this page in your browser to test its behavior.
Conclusion
When communicating with a web server, some processes may
take a significant amount of time to complete. During the processing time,
developers often want to protect themselves from impatient users who may
continue clicking the "submit" button in a futile attempt to speed up
the process. A popular way of dealing with this problem is to disable the
control that initiates the post to the server.
The ASP.NET runtime does not recognize a button that is
disabled before postback is sent to the server; therefore, if you want to
disable the button that originates the postback, you must "wire up" some
custom behavior that will produce the same result. This article demonstrated
how to send the request to the server and then disable the button after a short
interval. This technique works because the control is disabled after the
server is contacted and before the user observes any inconsistencies in the
appearance of the page.