在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); }
程式執行的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); }
這種做還蠻順的,只是真正要做的事要放到另外的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); }
在使用它去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:從零開始的軟體開發生活」
請大家繼續支持 ^_^