
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是此范例的运行画面。

图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; }