官术网_书友最值得收藏!

  • 決戰.NET
  • 黃忠成
  • 1520字
  • 2018-12-27 16:55:57

3.5 真實的Async-Postback進度顯示

雖然3.4節中已經告訴讀者如何實現工作中的進度回報,但如果要做的是Async-Postback運行時期的進度回報呢?在知道UpdateProgress控件可以在UpdatePanel控件進行刷新期間顯示信息、Timer控件可以定時顯示信息這兩點后,讀者們可能會想,將這兩者結合之后,是不是就能夠于UpdatePanel控件進行刷新期間,在UpdateProgress控件中顯示進度呢?答案是否定的,因為Timer控件有一個特性,不會在其他Async-Postback動作未完成前觸發,這也就是說,利用Timer控件來顯示進度的想法是無法實現的!這與前述之其他Async-Postback未完成前,UpdatePanel控件的再次刷新動作會遺失前次刷新結果的情況類似,這些現象都告訴了我們,當使用ASP.NET AJAX時,同時間只能有一個Async-Postback可完全正常運作。這個限制來自于ASP.NET AJAX的架構設計,稍后的章節會詳細討論為何會設計成這個樣子。那如果真有此需求,該如何做呢?既然ASP.NET AJAX不允許多個Async-Postback同時運行,那么我們就利用另一個不受限于此的機制,就是ASP.NET所提供的Callback機制,這個機制不會受限于ASP.NET AJAX,而且因為其簡單的設計,設計師會擁有較寬廣的控制空間。

1. 創建一個新網頁,命名為WorkingUpdateProgressWithReport.aspx。

2. 在頁面中加入一個ScriptManager控件。

3. 加入一個UpdatePanel控件,ID為UpdatePanel1。

4. 將UpdatePanel1控件的UpdateMode設為Conditional。

5. 加入一個UpdateProgress控件,ID為UpdateProgress1。

6. 在UpdatePanel控件中加入一個Button控件,ID為Button1。

7. 在UpdateProgress控件中加入一個Label控件,ID為Label1,Text為Updating...。

8. 在UpdateProgress控件中加入一個Label控件,ID為Label2。

9. 在Button1控件的Click事件中鍵入程序3-16中的Button1_Click內代碼。

10. 在此網頁的Page類實現ICallbackEventHandler界面,如程序3-11。

11. 在此網頁的網頁源碼中,鍵入程序3-12的JavaScript代碼。

程序3-11

    Samples\3\AjaxDemo1\WorkingUpdateProgressWithReport.aspx.cs
    using System;
    using System.Data;
    using System.Configuration;
    using System.Collections;
    using System.Web;
    using System.Web.Security;
    using System.Web.UI;
    using System.Web.UI.WebControls;
    using System.Web.UI.WebControls.WebParts;
    using System.Web.UI.HtmlControls;
    public partial class WorkingUpdateProgressWithReport : System.Web.UI.Page,
      ICallbackEventHandler
    {
        private string currentArgs = string.Empty;
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostback)
            {
                Guid guid = Guid.NewGuid();
                string script = "function CallServer(controlID,arg)\r\n"+
                                    "{\r\n"+
                                    "  WebForm_DoCallback(controlID,'"+
                                    guid.ToString()+"',ReceiveData,
                                    null,null,true);\r\n "+
                                    "  if(!isEnd)\r\n"+
                                    " window.setTimeout(\"CallServer
                                      ('__Page',null)\",1000);\r\n"+
                                    "}";
                //要求產生Callback的初始化程序代碼.
                Page.ClientScript.GetCallbackEventReference(this, "arg",
                    "ReceiveServerData", "context");
                Page.ClientScript.RegisterClientScriptBlock(typeof(Page),
                    "CallBackScript", script,true);
                ViewState["Current_TaskID"] = guid.ToString();
            }
        }
        protected void Button1_Click(object sender, EventArgs e)
        {
            Cache[((string)ViewState["Current_TaskID"]) + "$Button1_Progress"] = 0;
            for (int i = 0; i < 10; i++)
            {
                Cache[((string)ViewState["Current_TaskID"]) + "$Button1_Progress"]
                    = i * 10;
                System.Threading.Thread.Sleep(1000);
            }
        }
        #region ICallbackEventHandler Members
        public string GetCallbackResult()
        {
            if (Cache[currentArgs + "$Button1_Progress"] != null)
                    return Cache[currentArgs + "$Button1_Progress"].ToString();
            return string.Empty;
        }
        public void RaiseCallbackEvent(string eventArgument)
        {
            currentArgs = eventArgument;
        }
        #endregion
    }

程序3-12

    Samples\3\AjaxDemo1\WorkingUpdateProgressWithReport.aspx
    <%@ Page Language="C#" AutoEventWireup="true" CodeFile=
      "WorkingUpdateProgressWithReport.aspx.cs"
      Inherits="WorkingUpdateProgressWithReport" %>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.
      w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml" >
    <head runat="server">
        <title>Untitled Page</title>
    </head>
    <body>
        <form id="form1" runat="server">
        <div>
            <asp:ScriptManager ID="ScriptManager1" runat="server">
            </asp:ScriptManager>
            <script language=javascript>
            var prm = Sys.WebForms.PageRequestManager.getInstance();
            var isEnd = false;
            prm.add_initializeRequest(InitRequest);
            prm.add_endRequest(EndRequest);
            function InitRequest(sender,args)
            {
              window.setTimeout("CallServer('__Page',null)",1000);
            }
            function  EndRequest(sender,args)
            {
              _isEnd = true;
            }
            function ReceiveData(rvalue,context)
            {
              var lb = $get("Label2");
              lb.innerText = rvalue;
            }
            </script>
        </div>
              <asp:UpdatePanel ID="UpdatePanel1" runat="server">
                  <ContentTemplate>
                      <asp:Button ID="Button1" runat="server" OnClick=
                      "Button1_Click" Text="Button" />
                  </ContentTemplate>
              </asp:UpdatePanel>
              <asp:UpdateProgress ID="UpdateProgress1" runat="server">
                  <ProgressTemplate>
                      <asp:Label ID="Label1" runat="server" Text=
                      "Updating:"></asp:Label>
                      <asp:Label ID="Label2" runat="server" Text=
                      "Label"></asp:Label>
                  </ProgressTemplate>
              </asp:UpdateProgress>
        </form>
    </body>
    </html>

運行此程序并點擊按鈕后,將會看到進度表由1到10逐步地顯示,如圖3-10所示。

true

圖3-10

那這個程序究竟是如何實現這個功能的呢?在用戶點擊Button按鈕后,此程序模擬需長時間工作的情況,每次循環均延遲1 秒,并將計數的值放到Cache中,當有Callback回來時,GetCallbackResult函數便會被調用,此時傳回Cache中的計數值在客戶端顯示,這便是Server端的運作流程。此處有個很特別的設計,放入Cache時的鍵值是利用Guid.NewGuid所產生出來的,此鍵值為產生CallBack Script時所使用的參數,因此在Callback發生時調用RaiseCallbackEvent,此參數便會被送上來,接著GetCallbackResult以此參數為鍵值由Cache中取出計數值。在Client Script部分,一如以往一樣在Async-Postback前后掛載事件,值得注意的是,在InitRequest時,我們以JavaScript的setTimeout來創建一個Timer,這是JavaScript的Timer,別跟ASP.NET AJAX的Timer搞混了,setTimeout會于指定的延遲時間后運行傳入的程序代碼,此處便是運行Page_Load時產生的CallServer函數(JavaScript的),而CallServer函數會在運行完畢后再次調用setTimeout來繼續下一次的進度更新。當Callback完成后,ReceiveData函數會被調用,此時便用$get函數來取得Label2 對象,并設定其innerText值(在FireFox中,需改為設定innerHTML值),這個$get函數是ASP.NET AJAX Client Library所提供的,可以讓我們以HTML元素的名稱來找到所需要的HTML元素對象。

我們做了什么?

這一節所使用的是違背ASP.NET AJAX的設計架構之技巧,也就是逆天而行的手法,既是如此,又為何這么做呢?我會使用此手法的原因是因為任職顧問時期,有客戶詢問UpdatePanel控件刷新時的進度回報功能,原本我建議使用第3.4節的手法,但是客戶在幾天后回報此法不可行。在仔細看過程序后,我察覺到客戶在Thread中嘗試訪問頁面上的控件,而這是不對的行為,因為在啟動Thread后,Server端并不會等待Thread運行完畢,而是繼續原本的網頁繪制動作后送出,此時原有的頁面上控件均已被釋放掉了,訪問它們只會產生異常。所以在不改動原有程序的情況下,我提供了本節的手法,在這個手法中并未使用Thread,而在事件中也可正常訪問頁面中的控件。不過使用此手法時必須特別注意ScriptManager控件的AsyncPostbackTimeout屬性值的設定,若設得太短,很快就會產生異常,此屬性將于后面章節中詳細說明。

主站蜘蛛池模板: 平武县| 尼木县| 渑池县| 深泽县| 观塘区| 兴海县| 江孜县| 周至县| 和顺县| 房产| 定远县| 屏南县| 潮安县| 眉山市| 潜江市| 饶阳县| 旺苍县| 永年县| 八宿县| 潜山县| 宣武区| 普宁市| 岳阳县| 淅川县| 顺平县| 社旗县| 兴义市| 焦作市| 曲沃县| 潼南县| 西青区| 芜湖县| 交城县| 新巴尔虎左旗| 大同市| 社旗县| 徐汇区| 淄博市| 濮阳市| 中超| 内乡县|