决战.NET
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

5.5 与PageMethods合作!另一种进度条实现

前面曾以UpdateProgress搭配ASP.NET Callback技巧,实现Async-Postback的进度回报功能,在了解PageMethods功能的用法之后,读者们一定会思考,PageMethods与Callback技术同样不会受到单一页面同时只能有一个Async-Postback的限制,若用PageMethods来取代Callback技术,是否能更简单地做出进度条呢?这个答案是肯定的也是否定的,肯定的原因是PageMethods可以替代Callback技术来实现此功能,否定的原因是这样不见得会写较少的程序代码。不管如何,多学一种应用总是好的,程序5-12是以PageMethods取代Callback来实现进度条的.aspx源码,程序5-13是.aspx.cs的源码。

程序5-12

    Samples\5\AdvAjaxDemo\UpdateProgressReport_WithPageMethod.aspx
    <%@ Page Language="C#" AutoEventWireup="true"
        CodeFile="UpdateProgressReport_WithPageMethod.aspx.cs"
        Inherits="UpdateProgressReport_WithPageMethod" %>
    <!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"
              EnablePageMethods="True">
            </asp:ScriptManager>
            <script language=javascript>
            var prm = Sys.WebForms.PageRequestManager.getInstance();
            prm.add_endRequest(EndRequest);
            function  EndRequest(sender,args)
            {
                if($get('Button1').disabled == "disabled")
                {
                  $get('Button1').disabled = "";
                }
            }
            function OnSucceeded(result, userContext, methodName)
            {
                if (methodName == "GetCurrentProgress")
                {
                  if(result != -1)
                    adjustProgress(result);
                  window.setTimeout("getProgress();",1000);
                }
                else if(methodName == "CancelCurrentProgress")
                {
                  $get('Button1').disabled = "";
                }
            }
            function OnFailed(error, userContext, methodName)
            {
                if(error !== null)
                {
                  alert(error.get_message());
                }
            }
            function cancelProcess()
            {
              PageMethods.CancelCurrentProgress($get('Hidden1').
                value,OnSucceeded,OnFailed);
              $get('UpdateProgress1').style.display = "none";
              if (prm.get_isInAsyncPostback()) {
                  prm.abortPostback();
              }
            }
            function getProgress()
            {
              PageMethods.GetCurrentProgress($get('Hidden1').value,
                OnSucceeded,OnFailed);
            }
            function adjustProgress(step)
            {
              $get('progress_reserve').style.width = (100-step) + "%";
              $get('progress_reserve').innerHTML = step+"%";
              $get('progress_step').style.width = step + "%";
            }
            function runProcess()
            {
              adjustProgress(0);
              window.setTimeout("getProgress();",1000);
              $get("Button1").disabled = "disabled";
            }
            </script>
        </div>
            <asp:UpdatePanel ID="UpdatePanel1" runat="server" UpdateMode=
                "Conditional">
                <ContentTemplate>
                    <asp:Button ID="Button1" runat="server" OnClick="Button1_Click"
                    OnClientClick="runProcess()" Text="Run" UseSubmitBehavior=
                    "False" />
                    <input id="Hidden1" runat="server" type="hidden" />
                </ContentTemplate>
            </asp:UpdatePanel>
            <br />
            <asp:UpdateProgress ID="UpdateProgress1" runat="server"
              AssociatedUpdatePanelID="UpdatePanel1">
                <ProgressTemplate>
                    <table cellpadding=0 cellspacing=0 width="100px" height="20px"
                    style="border-width:thin; border-style:solid; border-
                    color:Black" bgcolor="white">
                    <tr>
                    <td id="progress_step" width="0%" bgcolor=blue></td>
                    <td id="progress_reserve" width="100%"
                      style="color:Black"></td>
                    </tr></table>
                    <br />
                    <input id="Button2" type="button" value="Cacnel"
                      onclick="cancelProcess()" />
                </ProgressTemplate>
            </asp:UpdateProgress>
        </form>
    </body>
    </html>

让笔者稍微解释一下UpdateProgressReport_WithPageMethod.aspx中的设计,这个页面中放了一个UpdatePanel及一个UpdateProgress控件,UpdatePanel控件中有一个Button控件,当用户点击此Button控件后,会先运行OnClientClick所指定的runProcess函数,这个函数会以window.setTimeout定时地调用PageMethods取得最新进度。PageMethods函数调用完毕,OnSucceeded函数会被调用,此处就将进度传给adjustProgress函数,交由它来调整画面上的进度显示。此处有一个小技巧,就是使用了TABLE来做出图形化的进度行,说来也简单,只是在一个TABLE中放入一列两栏,第一栏为当前进度,第二栏为保留进度,依序调整两栏的宽度就能轻易地做出图形进度行的效果。眼尖的读者们应该会查觉到此页面的UpdateProgress控件中有一个Button是用来取消现行操作的,当用户点击该按钮后,cancelProcess函数会被调用,此处会调用PageMethods中的CancelCurrentProgress函数来通知Server端取消当前操作,之后会取消目前进行中的Async-Postback。是的!这是真正的Async-Postback Cancel功能,比先前只是放弃返回值的做法好多了,图5-9是此范例的运行画面。

true

图5-9

程序5-13

    Samples\5\AdvAjaxDemo\UpdateProgressReport_WithPageMethod.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 UpdateProgressReport_WithPageMethod : System.Web.UI.Page
    {
        [System.Web.Services.WebMethod]
        public static int GetCurrentProgress(string taskID)
        {
            if (HttpRuntime.Cache[taskID] != null)
            {
                TaskInformation info = (TaskInformation)HttpRuntime.Cache[taskID];
                return info.Progress;
            }
            return -1;
        }
        [System.Web.Services.WebMethod]
        public static void CancelCurrentProgress(string taskID)
        {
            if (HttpRuntime.Cache[taskID] != null)
                HttpRuntime.Cache.Remove(taskID);
        }
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostback && !ScriptManager1.IsInAsyncPostback)
                  Hidden1.Value = Guid.NewGuid().ToString();
        }
        protected void Button1_Click(object sender, EventArgs e)
        {
            TaskInformation info = new TaskInformation();
            info.Progress = 0;
            Cache[Hidden1.Value] = info;
            for (int i = 0; i < 10; i++)
            {
                if (Cache[Hidden1.Value] == null) //cancel
                    break;
                info.Progress = i * 10;
                Cache[Hidden1.Value] = info;
                System.Threading.Thread.Sleep(2000);
            }
            Cache.Remove(Hidden1.Value);
        }
    }
    internal class TaskInformation
    {
        public int Progress;
    }