Friday, October 06, 2006

Solving The Double Post Problem On A Button

using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Text;

namespace Developmentor
{
    /// <summary>
    /// C. Fouquet: Oct 2006
    /// This button cannot be clicked more than once during a post.
    /// This solves the classic double post problem where a user clicks
    /// a button more than once quickly and therefore posts twice!
    /// The idea is simple. Disable the button IMMEDIATELY after the click and
    /// THEN post. For that, we need to set a short timer to post very quickly after the
    /// button is disabled and give the illusion that the click is immediate.
    /// </summary>
    public class OneClickButton : Button
    {
        public int DelayInMs
        {
            get
            {
                if (ViewState["DelayInMs"] == null) return 20;
                return (int)ViewState["DelayInMs"];
            }
            set
            {
                ViewState["DelayInMs"] = value;
            }
        }

        protected override void Render(HtmlTextWriter writer)
        {
            string DisableMySelf = "document.getElementById('" + this.ClientID + "').disabled = true;";
            string PostIt = Page.ClientScript.GetPostBackEventReference(this, string.Empty);

            string FunctionName = "DelayedClick" + this.ClientID;
            StringBuilder sb = new StringBuilder();
            sb.Append("<script type=text/javascript >"); sb.Append(Environment.NewLine);
            sb.Append("function "); sb.Append(FunctionName); sb.Append("()"); sb.Append(Environment.NewLine);
            sb.Append("{"); sb.Append(Environment.NewLine);         
            sb.Append(PostIt); sb.Append(";"); sb.Append(Environment.NewLine);
            sb.Append("}"); sb.Append(Environment.NewLine);
            sb.Append("</script>"); sb.Append(Environment.NewLine);
            this.Attributes.Add("onclick", DisableMySelf + ";" + "setTimeout('" + FunctionName + "()',"+ DelayInMs.ToString() + ");return false");           
            writer.Write(sb.ToString()); // Write the javascript FIRST to it is available on click!
            base.Render(writer);           
        }             
    }
}