ASP.NET Postback前檢查的方式

  • 29318
  • 0

在Web Page上有時需要在按下Button時,先依畫面上的值,回Server端檢查後,再產生一段Javascript到Client,詢問使用者是否繼續。例如,畫面上的輸入值如果在警告範圍內,就要警告使用者,是否繼續這個動作。

前言

在Web Page上有時需要在按下Button時,先依畫面上的值,回Server端檢查後,再產生一段Javascript到Client,詢問使用者是否繼續。例如,畫面上的輸入值如果在警告範圍內,就要警告使用者,是否繼續這個動作。

針對這種行為,我整理以下3種方式,分別是使用XMLHttpRequest, doPostback 及Callback三種。

實作

XMLHttpRequest

這種做法在ASP時代常常使用, 在Button的onClick事件中先使用xmlHttp去Call 另一支網頁,然後取得該網頁的回傳值,再來詢問使用者。

AskContinue1.aspx定義button的事件

在Javascript中使用xmlhttpRequest

    var xhr = new XMLHttpRequest();
    xhr.open("POST", "xmlHttpCheck.aspx?p1=1&p2=2" , false );
    xhr.send(null)
    if (xhr.responseText == "1") {
        //詢問User是否要submit ?
         return confirm("警告!是否要submit?")
    } else {
    alert("檢查錯誤! 不能Submit!");
    return false;
    }
}
xmlHttpCheck.aspx.vb中做一些判斷及Render
protected void Page_Load(object sender, EventArgs e){
    // 先做一些Check後,再Render出選項
    Response.Write("1");
}

 

在AskContinue1.aspx.vb中寫下btnRun的Click事件處理要處理的Logic,範例只顯示出訊息。
protected void btnRun_Click(object sender, EventArgs e){
    ScriptManager.RegisterStartupScript(this, this.GetType(), "btnRun", "alert('Ask OK to Process!');", true);
}
Ask1_1 
Ask1_2 
程式執行的Path為AskContinue1.aspx btnRun’s JS=>xmlHttpCheck.aspx=>AskContinue1.aspx btnRun’s JS=>AskContinue1.aspx btnRun’s Server onClick
 

doPostback

這種做法是ASP.NET常常用的一招,直接把檢查Logic寫在Button的Click之中,有需要詢問User的,再Render出JS,然後再JS中去呼叫__doPostBack到Server端,而在Page_Load時判斷Request.Form["__EVENTARGUMENT"]的值,再去做原本Buton真正要做的事。

AskContinue2.aspx定義button的事件

<asp:Button ID="btnRun" runat="server" Text="Run" onclick="btnRun_Click"  />
protected void btnRun_Click(object sender, EventArgs e){
      // check first  & ask user
    ScriptManager.RegisterStartupScript(this, this.GetType(), "askuser", "if(confirm('警告!是否要submit?')){window.setTimeout('__doPostBack(\"" + UpdatePanel1.UniqueID + "\", \"ProcbtnRun\");', 500, 'Javascript')};", true);
}
在Page_Load再去判斷是否為我們自行呼叫的postback
 
protected void Page_Load(object sender, EventArgs e){
    //判斷是否由user確定要繼續處理?
    if (Page.IsPostBack)
    {
        if (Request.Form["__EVENTARGUMENT"] == "ProcbtnRun")
            ProcbtnRun();
    }
}

void ProcbtnRun()
{
    ScriptManager.RegisterStartupScript(this, this.GetType(), "btnRun", "alert('Ask OK to Process!');", true);
}

 

ask2_1

ask2_2

這種做還蠻順的,只是真正要做的事要放到另外的Method之中。 另外,您會發現我沒有直接呼叫__doPostBack的Method,而是用setTimeout包起來,那是因為,在某些情形下,直接Call __doPostBack會沒有反應,所以才用setTimeout。

Callback

這種方式與xmlHttpRequest相同,只是檢查的Logic寫在本頁的Callback的事件之中。那為何要用這種方式呢? 我想可能是因為如果使用xmlHttpRequest的方式,要把需要判斷的一些物件都傳送給檢查的頁面,如果用Callback的話,它是在本頁檢查,所以完全取得到該頁面的資料,就省去了傳參數的部份,不過,相對的,它的處理是比較麻煩的哦!

要用Callback的話要先實作ICallbackEventHandler Interface,然後在Page_Load時去註冊btnRun的OnClientClick事件會去做Callback。因為Callback檢查後,會再詢問User是否要執行,所以,我們要在Callback的JS內加入btnRun的postback script。

protected void Page_Load(object sender, EventArgs e)  {
    //註冊Callback
    //取得btnRun Postback的Script
    string btnRunPostBackScript = ClientScript.GetPostBackEventReference( btnRun, "" );
    //設定Callback回來要執行的Function Name
    string btnRunCallbackHandlerFunName = string.Format("{0}_CallbackHandler", btnRun.ClientID);
    //設定Callback回來要執行的JS(詢問User是否要Submit,Yes就執行btnRun的postback)
    string btnABScript = "function    " + btnRunCallbackHandlerFunName + "(responseText, context){" + 
            "if(responseText=='1'){" + 
                "if(confirm('警告!是否要submit?'))" +
                "{" + btnRunPostBackScript + "; return false;}" + 
                "}" +
            "}";
    ClientScript.RegisterClientScriptBlock(Page.GetType(), btnRunCallbackHandlerFunName, btnABScript, true);
    //最後加上return false才不會postback
    btnRun.OnClientClick = ClientScript.GetCallbackEventReference(Page, "'RunCallback'", btnRunCallbackHandlerFunName, "") + "; return false;";
}

 

實作Callback的Interface
//實作Callback Interfacepublic string _CallResult = string.Empty;
//將Callback的值回傳
string ICallbackEventHandler.GetCallbackResult()
{
    return _CallResult;
}

//處理Callback
void ICallbackEventHandler.RaiseCallbackEvent(string arg)
{
    // 先做一些Check後,再Render出選項
    _CallResult = "1";
}

 

實際btnRun要做的事如下
//真正btnRun要做的事protected void btnRun_Click(object sender, EventArgs e)
{
    ScriptManager.RegisterStartupScript(this, this.GetType(), "btnRun", "alert('Ask OK to Process!');", true);
}
ask3_1 
ask3_2 
ask3_3 
 在使用它去Call Run button的doPostback時,又發生了「錯誤: '__pendingCallbacks[...].async'
 是 null 或不是一個物件」的Javascript錯誤,上網找到了Fixing "Microsoft JScript runtime error: '__pendingCallbacks[...].async' is null or not an object"
,改了一下JS,Callback才正常。另一個Callback取值的問題,可以參考我上一篇的文章(Asp.NET 2.0 Callback無法讀到其他input現值問題與解決方式),裡面有說明!
//http://www.dotblogs.com.tw/rainmaker/archive/2009/09/30/10847.aspx//先把舊的Function存起來!
var __oldWebForm_DoCallback = WebForm_DoCallback;
//覆寫MS的Callback Function以解決在Server端無法取得其他input的現值
WebForm_DoCallback = function newWebForm_DoCallback(eventTarget, eventArgument, eventCallback, context, errorCallback, useAsync) {
    // add 
    __theFormPostData = "";
    WebForm_InitCallback();
    // add
    __oldWebForm_DoCallback(eventTarget, eventArgument, eventCallback, context, errorCallback, useAsync);
}

//http://www.codeproject.com/KB/aspnet/pendingcallbacks.aspx
function WebForm_CallbackComplete() {
    // the var statement ensure the variable is not global
    for (var i = 0; i < __pendingCallbacks.length; i++) {
        callbackObject = __pendingCallbacks[i];
        if (callbackObject && callbackObject.xmlRequest &&
            (callbackObject.xmlRequest.readyState == 4)) {
            // SyncFixed: line move below 
            // WebForm_ExecuteCallback(callbackObject);
            if (!__pendingCallbacks[i].async) {
                __synchronousCallBackIndex = -1;
            }
            __pendingCallbacks[i] = null;
            var callbackFrameID = "__CALLBACKFRAME" + i;
            var xmlRequestFrame = document.getElementById(callbackFrameID);
            if (xmlRequestFrame) {
                xmlRequestFrame.parentNode.removeChild(xmlRequestFrame);
            }
            // SyncFixed: the following statement has been moved down from above;
            WebForm_ExecuteCallback(callbackObject);
        }
    }
} 

 

結論

以上的3種方式,筆者最常用的是doPostback,因為比較簡單! 如果各位有其它方式,麻煩請讓我知道,謝謝!

附上範例程式:CallBack.rar

Hi, 

亂馬客Blog已移到了 「亂馬客​ : Re:從零開始的軟體開發生活

請大家繼續支持 ^_^