一步步打造防止重複提交按鈕

首先說說防止重複提交按鈕是啥東西:
我們在訪問有的網站,輸入表單完成以後,單擊提交按鈕進行提交以後,提交按鈕就會變爲灰色,用戶不能再單擊第二次,直到重新加載頁面或者跳轉。這樣,可以一定程度上防止用戶重複提交導致應用程序上邏輯錯誤。有朋友說,這個按鈕完全可以用js來做,是的。不過當你需要大量這種按鈕時,是否爲每一個都去編程而不封裝一個呢?
另外,爲了增加其功能性,我們除了讓他有防止重複提交的功能以外,還可以給他彈出提示框,就像單擊刪除按鈕時,用戶會受到一個提示再次確認是否真的刪除。
 
好,接下來,第一步:知識儲備,
我們知道,要防止重複提交,要在客戶端設法使用戶單擊一次以後按鈕變灰,這種客戶端行爲顯然只能藉助js代碼來完成,服務器端運行的ASP.NET是做不到的。那要讓客戶端用戶單擊按鈕後立即收到一條再次確認消息,在確認之前不會提交到服務器,也需要js代碼。因此,我們基本的絲路就是在頁面加載按鈕時,一併把所需的js代碼發送到客戶端去。而該按鈕都是適用js腳本,因此不影響服務器端行爲。
 
第二步:從Button繼承。
因爲它是一個按鈕,擁有按鈕所需要的全部特徵屬性,因此我們就從System.Web.UI.WebControls.Button這個類繼承。而防止重複提交按鈕在按鈕變成灰色以後,應該顯示什麼文本呢?新增一個AfterSubmitText屬性來指示;採用一個Bool值“ShowMessageBox”屬性來確定是否需要在客戶端彈出提示;使用WarningText屬性來指示客戶端彈出提示的內容。
 
第三步:
現在,我們就來改寫AddAttributesToRender方法,ASP.NET在渲染該控件到輸出時,會調用該方法我們所改寫的方法,以達到將JS代碼發送到客戶端的目的。具體代碼如下:

protected override void AddAttributesToRender(HtmlTextWriter writer)
         {
             System.Text.StringBuilder ClientSideEventReference = new System.Text.StringBuilder();
 
            if (((this.Page != null) && this.CausesValidation) && (this.Page.Validators.Count > 0))
             {
                 ClientSideEventReference.Append("if (typeof(Page_ClientValidate) == 'function'){if (Page_ClientValidate() == false){return false;}}");
             }
             //ShowMessageBox?
             if (this.ShowMessageBox)
             {
                 ClientSideEventReference.Append("if (!confirm('" + this.WarningText + "')){return false}");
             }
             ClientSideEventReference.AppendFormat("this.value = '{0}';", (string)this.ViewState["afterSubmitText"]);
             ClientSideEventReference.Append("this.disabled = true;");
             ClientSideEventReference.Append(this.Page.ClientScript.GetPostBackEventReference(this, string.Empty));
 

            writer.AddAttribute(HtmlTextWriterAttribute.Onclick, ClientSideEventReference.ToString(), true);
             base.AddAttributesToRender(writer);
         }


 

我們把發送到客戶端的js代碼看作是一個字符串,爲了提高性能,用StringBuilder來對象來構造這個字符串。
 首先根據頁面對象存在且控件啓用了驗證(該屬性從父繼承),且頁面對象的驗證器內計數大於0來決定輸出一段引發驗證的js代碼,
 根據ShowMessageBox屬性來決定輸出一個彈出提示的代碼,並且彈出提示的內容由WarningText屬性給出。
 書寫一個js腳本爲按鈕賦值爲提交後文本,並且將按鈕設置爲禁用以變灰色。
 最後附加一段由ASP.NET提供的用於影射回調事件引用的js腳本(this.Page.ClientScript.GetPostBackEventReference(this, string.Empty)方法將返回一個回送事件引用的js腳本字符串)
 
將StringBuilder對象內構造的字符串用HtmlTextWriter對象的AddAttribute方法寫入按鈕的OnClick事件中。
 
調用父類的AddAttributesToRender方法讓父類有機會完成其他的配置等操作。
 

完整的ClickOnceButton代碼如下:

using System;
 using System.Collections.Generic;
 using System.ComponentModel;
 using System.Text;
 using System.Web;
 using System.Web.UI;
 using System.Web.UI.WebControls;
 
namespace BlogLan.Web.Controls
 {
     /// <summary>
     /// 表示一個防止重複提交的按鈕。當用戶單擊按鈕以後,該按鈕變灰,不能再次單擊,直到重新加載頁面或者跳轉。
     /// </summary>
     [DefaultProperty("Text")]
     [ToolboxData("<{0}:ClickOnceButton runat=server></{0}:ClickOnceButton>")]
     public class ClickOnceButton : System.Web.UI.WebControls.Button
     {
         /// <summary>
         /// 默認的構造函數。
         /// </summary>
         public ClickOnceButton()
         {
             this.ViewState["afterSubmitText"] = "正在提交,請稍候...";
             base.Text = "ClickOnceButton";
             this.ViewState["showMessageBox"] = false;
             this.ViewState["warningText"] = "確定要提交嗎?";
         }
 
        /// <summary>
         /// 獲取或設置單擊按鈕後,按鈕上所顯示的文本。
         /// </summary>
         [Bindable(true),
         Category("Appearance"),
         DefaultValue("正在提交,請稍候..."),
         Description("指示單擊提交後,按鈕上所顯示的文本。")]
         public string AfterSubmitText
         {
             get
             {
                 string afterSubmitText = (string)this.ViewState["afterSubmitText"];
                 if (afterSubmitText != null)
                 {
                     return afterSubmitText;
                 }
                 else
                 {
                     return string.Empty;
                 }
             }
             set
             {
                 this.ViewState["afterSubmitText"] = value;
             }
         }
 
        [Bindable(true),
         Category("Appearance"),
         DefaultValue(false),
         Description("指示是否要顯示一個提示框。")]
         public bool ShowMessageBox
         {
             get
             {
                 return (bool)this.ViewState["showMessageBox"];
             }
             set
             {
                 this.ViewState["showMessageBox"] = value;
             }
         }
 

        [Bindable(true),
         Category("Appearance"),
         DefaultValue("確定要提交嗎?"),
         Description("指示提示框內所包含的內容。")]
         public string WarningText
         {
             get
             {
                 return (string)this.ViewState["warningText"];
             }
             set
             {
                 this.ViewState["warningText"] = value;
             }
         }
 
        /// <summary>
         /// AddAttributesToRender
         /// </summary>
         /// <param name="writer">HtmlTextWriter</param>
         protected override void AddAttributesToRender(HtmlTextWriter writer)
         {
             System.Text.StringBuilder ClientSideEventReference = new System.Text.StringBuilder();
 
            if (((this.Page != null) && this.CausesValidation) && (this.Page.Validators.Count > 0))
             {
                 ClientSideEventReference.Append("if (typeof(Page_ClientValidate) == 'function'){if (Page_ClientValidate() == false){return false;}}");
             }
             //ShowMessageBox?
             if (this.ShowMessageBox)
             {
                 ClientSideEventReference.Append("if (!confirm('" + this.WarningText + "')){return false}");
             }
             ClientSideEventReference.AppendFormat("this.value = '{0}';", (string)this.ViewState["afterSubmitText"]);
             ClientSideEventReference.Append("this.disabled = true;");
             ClientSideEventReference.Append(this.Page.ClientScript.GetPostBackEventReference(this, string.Empty));
 

            writer.AddAttribute(HtmlTextWriterAttribute.Onclick, ClientSideEventReference.ToString(), true);
             base.AddAttributesToRender(writer);
         }
     }
 }


 

你可以把它編譯爲dll,放置在工具箱中,隨意拖放到網頁上即可使用。因爲繼承了Button控件,它擁有Button的全部特性,並且自動繼承了Button的設計時支持。
 

後記:
 當我決定自己開發自定義控件時,這是我第一個想到的,因爲明確它就是在一個Button控件基礎上,並用JS代碼來實現功能。因此,它應該是一個比較簡單的東西了。
 代碼貼出來,與各位分享,儘管現在新技術層出不窮,用AJAX也可以達到效果,不過假如你只是想要防止重複提交,也不用勞煩AJAX這個沉重的框架來完成,有點殺雞用牛刀了。
 願各位朋友舉一反三,深入探討。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章