ScriptManager和UpdatePanel兩個控制項已經能夠實現了用戶端與伺服器端的非同步通訊了。要想對非同步作業進一步控制的話,那我們還得進一步研究PageReqeustManager類。 PageRequestManager類是用戶端的類,用於協調ScriptManager和UpdatePanel控制項,管理頁面上的非同步更新操作。通過PageRequestManager用戶端的執行個體我們可以深入到在用戶端頁面生命週期中,更細緻地操作用戶端的頁面。 一、PageRequestManager執行個體: 要想在用戶端獲得PageRequestManager執行個體,頁面上必須擁有一人ScriptManager控制項,並且ScriptManager控制項的EnablePartialRendering屬性必須設為True。 只要頁面上內含一個EnablePartialRendering屬性為True的ScriptManager控制項,該頁面就會自動建立一個PageRequestManager執行個體。程式員不需要自行建立PageRequestManager執行個體,直接取來用即可。 取得PageRequestManager執行個體的代碼: var prm = Sys.WebForms.PageRequestManager.getInstance(); 屬性prm.get_isInAsyncPostBack():判斷一個非同步回送是否進行中中。 方法prm.abortPostBack():把一個正在執行中的非同步回送取消。 二、用戶端頁面的生命週期: PageRequestManager類的優勢就是能夠讓程式員深入至用戶端頁面的生命週期中去。所以要想充分發揮PageRequestManager類的功能,那首先要瞭解非同步頁的生命週期。 1、initializeRequest: 觸發時機:當一個非同步請求的回送被初始化之前引發。 添加事件處理代碼:Sys.WebForms.PageRequestManager.getInstance().add_initializeRequest(initFunc); 移除事件處理代碼:Sys.WebForms.PageRequestManager.getInstance().remove_initializeRequest(initFunc); initFunc是該頁面初始化之前要執行的用戶端方法。該方法的聲明為: function initFunc(sender,args) { //args的資料類型是:InitializeRequestEventArgs類型。 //args.get_postBackElement():取得初始化非同步回送的元素對象。 //args.get_postBackElement().id取得初始化非同步回送的元素對象的id號 //args.get_postBackElement().value取得初始化非同步回送的元素對象的value值 //args.set_cancel(bool):未初始化非同步回送,即丟棄該非同步回送。 } 如果非同步處理的過程比較慢,在非同步處理的過程中再次發出同樣的請求的話,那後者的非同步處理請求會取消掉前一步未處理完的請求。這就是預設的非同步請求優先順序--“後者優先”。 我們通常回利用initailizeRequest事件來取消一個非同步回送(進行中的回送和將要初始化的回送)。 2、beginRequest: 觸發時機:在非同步請求初始化完成,且向伺服器提出請求之前引發。 添加事件處理代碼:Sys.WebForms.PageRequestManager.getInstance().add_beginRequest(beginFunc); 移除事件處理代碼:Sys.WebForms.PageRequestManager.getInstance().remove_beginRequest(beginFunc); beginFunc是向伺服器提出請求之前要執行的用戶端方法。該方法的聲明為: function beginFunc(sender,args) { //args的資料類型是:BeginRequestEventArgs類型。 //args.get_postBackElement():取得初始化非同步回送的元素對象。 } 我們通常在beginRequest事件中設定一個標題,或是啟始化一個動化告知使用者進行中請求處理。 3、pageLoading: 觸發時機:非同步回送已經被伺服器接收並響應,但還沒有對頁面進行任何更新之前引發 添加事件處理代碼:Sys.WebForms.PageRequestManager.getInstance().add_pageLoading(loadingFunc); 移除事件處理代碼:Sys.WebForms.PageRequestManager.getInstance().remove_pageLoading(loadingFunc); loadingFunc是頁面更新之前要執行的用戶端方法。該方法的聲明為: function loadingFunc(sender,args) { //args的資料類型是:PageLoadingEventArgs類型。 //args代表內容將要被更新或刪除的UpdatePanel控制項的<div>。 //var arr = args.get_panelsDeleting(); 取得將被刪除的各個UpdatePanel控制項的<div> //var arr = args.get_panelsUpdating();取得將被更新的各個UpdatePanel控制項的<div> } 4、pageLoaded: 觸發時機:回送完成頁面地區被更新之後引發。 添加事件處理代碼:Sys.WebForms.PageRequestManager.getInstance().add_pageLoaded(loadedFunc); 移除事件處理代碼:Sys.WebForms.PageRequestManager.getInstance().remove_pageLoaded(loadedFunc); loadedFunc是頁面更新後要執行的用戶端方法。該方法的聲明為: function loadedFunc(sender,args) { //args的資料類型是:PageLoadedEventArgs類型 //args代表更新的或建立的UpdatePanel控制項的<div> //var arr = args.get_panelsUpdated();取得被更新的各個UpdatePanel控制項的<div> //var arr = args.get_panelsCreated();取得新建立的各個UpdatePanel控制項的<div> } 5、endRequest: 觸發時機:回送請求處理完畢後,就會引發endRequest事件。 添加事件處理代碼:Sys.WebForms.PageRequestManager.getInstance().add_endRequest(endFunc); 移除事件處理代碼:Sys.WebForms.PageRequestManager.getInstance().remove_endRequest(endFunc); endFunc是頁面請求完成後執行的用戶端方法。該方法的聲明為: function endRequest(sender,args) { //args的資料類型是:EndRequestEventArgs類型 //var err = args.get_error():判斷是否發生錯誤,並取得錯誤對象。 //var em = args.get_error().message:取得錯誤的出錯資訊。 //args.set_errorHandled(true):設定錯誤已被處理。 //var gm = args.get_errorHandled():判斷錯誤是否被處理。 //非同步請求發生異常後,如果程式員不在用戶端捕獲處理的話,PageRequestManager對象會將其以對話方塊的形式彈出異常的資訊。如果程式員想自己編寫錯誤處理代碼,而不交由PageRequestManager對象處理的話。那程式員可以通過args.get_error().message屬性取得錯誤資訊,然後編寫異常處理代碼,最後記得執行args.set_errorHandled(true)。這樣就阻止異常繼續回返給PageRequestManager對象。具體使用在後面將詳細說明。 } 三、案例: 1、非同步回送的優先順序-後者的優先順序高於前者: 如果處理非同步回送用的時間很長的話,那麼在處理第一個回送的過程中,用戶端又產生第二次非同步回送的話話,那後引發的回送回取消先引發的回送。 頁面上有兩個非同步按鈕,產生非同步回送,為了拉長伺服器端處理回送的時間,我分別在兩個按鈕的伺服器click事件中使用線程休眠了10秒和5秒,然後再向lblInfo標籤中輸入處理結果。 <asp:Button ID="btnLong" runat="server" Text="10秒後產生響應" OnClick="btnLong_Click" /> <asp:Button ID="btnShort" runat="server" Text="5秒後產生響應" OnClick="btnShort_Click" /> <asp:ScriptManager ID="ScriptManager1" runat="server"> </asp:ScriptManager> <asp:UpdatePanel ID="UpdatePanel1" runat="server"> <ContentTemplate> <asp:Label ID="lblInfo" runat="server" BackColor="#C0C0FF" Width="100%"></asp:Label> </ContentTemplate> <Triggers> <asp:AsyncPostBackTrigger ControlID="btnLong" EventName="Click" /> <asp:AsyncPostBackTrigger ControlID="btnShort" EventName="Click" /> </Triggers> </asp:UpdatePanel> 為了讓大家看清楚後一步引發會取消掉前一步的引發,我在PageRequestManager對象中加入了beginRequest事件處理代碼,在發送請求前在頁面上顯示請求寄件者 。 var prm = Sys.WebForms.PageRequestManager.getInstance(); prm.add_beginRequest(beginR); function beginR(sender,args) { var d = $get("lblInfo"); var t = args.get_postBackElement().value; d.innerHTML = "正在處理同"+t+"引發的回送"; } 伺服器端的代碼: protected void Page_Load(object sender, EventArgs e) { ScriptManager1.RegisterAsyncPostBackControl(this.btnLong); ScriptManager1.RegisterAsyncPostBackControl(this.btnShort); } protected void btnLong_Click(object sender, EventArgs e) { System.Threading.Thread.Sleep(10000); lblInfo.Text = ((Button)sender).Text + "產生的響應"; } protected void btnShort_Click(object sender, EventArgs e) { System.Threading.Thread.Sleep(5000); lblInfo.Text = ((Button)sender).Text + "產生的響應"; } 效果:當點擊"10秒後產生響應"按鈕的時候,在lblInfo中會顯示"正在處理同10秒後產生響應引發的回送",如果此時你再點擊"5秒後產生響應"按鈕的時候,lblInfo的顯示會變成"正在處理同5秒後產生響應引發的回送"。此時第一次引發的回送回被取消,等待5後頁面上會顯示出"5秒後產生響應產生的響應",並不出現第一次回送的伺服器響應。 2、取消非同步回送: 取消非同步回送分兩種: a. 取消自在執行的非同步回送-通過調用PageRequestManager對象的abortPostback()方法來取消。 b. 取消新產生的異點回送-通過設定InitializeRequestEventArgs對象的cancel屬性為true來取消。 下面是一個查詢汽車資訊的介面,為了拉長伺服器的處理時間,我在查詢按鈕中加入了6秒中的休眠時間。 在點擊查詢的時候,為了不讓使用者乾等,我加入了一個<div>,提示使用者請求正在處理中,在<div>中加入一個"取消"按鈕,當使用者點擊 "取消"按鈕的時候,可以中止伺服器端的非同步處理。 等到伺服器處理完畢非同步請求後顯示下面的介面 如果在伺服器未處理完非同步請求時,使用者再點擊"查詢"按鈕,做到防止後引發的回送衝掉第一次引發的回送,並在介面中加入提示資訊。 介面設計: HTML代碼如下: <asp:ScriptManager ID="ScriptManager1" runat="server" AsyncPostBackErrorMessage="這是一個自訂的小異常"> </asp:ScriptManager> <asp:DropDownList ID="ddl" runat="server" DataSourceID="SqlDataSource1" DataTextField="prod_name" DataValueField="prod_code" Width="194px"> </asp:DropDownList> <asp:Button ID="Button1" runat="server" Text="查詢" OnClick="Button1_Click" /> <asp:SqlDataSource ID="SqlDataSource1" runat="server" ConnectionString="<%$ ConnectionStrings:mydbConnectionString %>" SelectCommand="SELECT [prod_code], [prod_name] FROM [productor]"></asp:SqlDataSource> <asp:UpdatePanel ID="UpdatePanel1" runat="server"> <ContentTemplate> <div id="divInfo" style="background-color: #ff99ff; display: none;"> <asp:Button ID="btnCancel" runat="server" Text="取消" /></div> <br /> <asp:GridView ID="list" runat="server" Width="100%" DataSourceID="SqlDataSource2"> </asp:GridView> <asp:SqlDataSource ID="SqlDataSource2" runat="server" > </asp:SqlDataSource> </ContentTemplate> <Triggers> <asp:AsyncPostBackTrigger ControlID="Button1" EventName="Click" /> </Triggers> </asp:UpdatePanel> CS代碼如下: protected void Page_Load(object sender, EventArgs e) { ScriptManager1.RegisterAsyncPostBackControl(this.Button1); } protected void Button1_Click(object sender, EventArgs e) { System.Threading.Thread.Sleep(6000); string str = WebConfigurationManager.ConnectionStrings["mydbConnectionString"].ToString(); SqlDataSource2.ConnectionString = str; SqlDataSource2.SelectCommand = "select car.ids,car.name,car.price,brand.brand_name,prod_code from car join brand on car.brand=brand_code where prod_code=@p"; SqlDataSource2.SelectParameters.Clear(); SqlDataSource2.SelectParameters.Add("p",ddl.SelectedValue); SqlDataSource2.Select(DataSourceSelectArguments.Empty); } 用戶端JS代碼的實現: var prm = Sys.WebForms.PageRequestManager.getInstance(); //取得PageRequestManager對象的執行個體 prm.add_initializeRequest(init); //添加對象初始化事件處理常式 function init(sender,args) //事件處理常式 { //如果在非同步處理過程中,點擊了"取消"按鈕的話,就中止正在處理的非同步處理 。 if(prm.get_isInAsyncPostBack() && args.get_postBackElement().id=="btnCancel") { prm.abortPostBack(); //中止非同步處理 } //如果在非同步處理過程中,又點擊了一次"查詢"按鈕的話,就取消新的請求。 else if(prm.get_isInAsyncPostBack() && args.get_postBackElement().id=="Button1") { args.set_cancel(true); //取消新的請求 var d = $get("divInfo"); d.style.display = ""; d.innerHTML += "<br>仍然正在請求中,請稍候"; } //如果沒有非同步處理正在執行,使用者點擊了"查詢"按鈕的話,就顯示"正在請求中,請稍候" else if(!prm.get_isInAsyncPostBack() && args.get_postBackElement().id=="Button1") { var d = $get("divInfo"); d.style.display = ""; d.innerHTML += "<br>正在請求中,請稍候"; } } 3、非同步請求自訂的錯誤處理。 在非同步請求處理中如果產生錯誤,系統預設會彈一個瀏覽的對話方塊,告訴使用者的出錯資訊。有時候我們並不想用這種預設的異常處理介面,那如何處理呢? 實現步驟: a、編寫ScriptManager對象的伺服器端事件ScriptManager1_AsyncPostBackError代碼,把捕獲的錯誤資訊賦給ScriptManager對象的AsyncPostBackErrorMessage屬性。 b、添加PageRequestManager對象的endRequest事件處理常式。在處理常式中用args.get_error().message取出運行中的錯誤資訊。然後在指定的<Div >中把錯誤資訊顯示出來。 c、用set_errorHandled(true)把錯誤對象標記為已處理,防止錯誤對象繼續冒泡給瀏覽器。 介面: HTML代碼: <asp:ScriptManager ID="ScriptManager1" runat="server" OnAsyncPostBackError="ScriptManager1_AsyncPostBackError"> </asp:ScriptManager> <br /> <asp:Button ID="btnOK" runat="server" OnClick="btnOK_Click" Text="成功送出" /> <asp:Button ID="btnError" runat="server" OnClick="btnError_Click" Text="送出失敗" /></div> <asp:UpdatePanel ID="UpdatePanel1" runat="server"> <ContentTemplate> <asp:Label ID="lblInfo" runat="server" BackColor="#C0C0FF" Width="100%"></asp:Label> </ContentTemplate> <Triggers> <asp:AsyncPostBackTrigger ControlID="btnError" EventName="Click" /> <asp:AsyncPostBackTrigger ControlID="btnOK" EventName="Click" /> </Triggers> </asp:UpdatePanel> CS代碼: protected void Page_Load(object sender, EventArgs e) { ScriptManager1.RegisterAsyncPostBackControl(btnOK); ScriptManager1.RegisterAsyncPostBackControl(btnError); } protected void btnOK_Click(object sender, EventArgs e) { lblInfo.Text = "非同步回送成功"; } protected void btnError_Click(object sender, EventArgs e) { Exception ex = new Exception("這是一個自訂的使用者異常資訊"); throw ex; } protected void ScriptManager1_AsyncPostBackError(object sender, AsyncPostBackErrorEventArgs e) { ScriptManager1.AsyncPostBackErrorMessage += e.Exception.Message; } 用戶端JS代碼: var prm = Sys.WebForms.PageRequestManager.getInstance(); prm.add_endRequest(endfunc); function endfunc(sender,args) { if(args.get_error() != null) { var em = args.get_error().message; args.set_errorHandled(true); $get("lblInfo").innerHTML = em; } } |