首先說說防止重複提交按鈕是啥東西:
我們在訪問有的網站,輸入表單完成以後,單擊提交按鈕進行提交以後,提交按鈕就會變爲灰色,用戶不能再單擊第二次,直到重新加載頁面或者跳轉。這樣,可以一定程度上防止用戶重複提交導致應用程序上邏輯錯誤。有朋友說,這個按鈕完全可以用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這個沉重的框架來完成,有點殺雞用牛刀了。
願各位朋友舉一反三,深入探討。