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

3.4 真实进度的呈现:使用Timer与UpdatePanel控件

倘若某一个Async-Postback动作需要花费较多时间来处理,例如批次新增或转文件等作业,客户多半不喜欢网页就此停滞,而希望网页能在运行期间定时回报目前的处理进度。以转档作业为例,以往实现这类需求时,多半是将整个转档动作放到Thread(线程)中,并产生一个工作ID,存放在事先创建于网页中的Hidden Field内,用来作为访问Cache信息的键值,当Thread运行时,会依据此键值将进度填入Cache中,网页则是利用JavaScript的setTimeout函数,定时做Postback,再在Server端所送上来的Hidden Field中取得工作ID,进而取出Cache中的进度信息显示。时至今日,这种处理进度回报的流程仍然可行,不过有了UpdatePanel及Timer等控件的协助,定时Postback的动作便可轻松升级为定时Async-Postback动作,让客户不会再有网页画面闪动的不舒服感受,请照着下列步骤做。

1. 创建一个新网页,命名为AsyncPostbackProgressReport.aspx。

2. 在页面中放入ScriptManager控件。

3. 放入UpdatePanel控件,命名为UpdatePanel1。

4. 将UpdatePanel1控件的UpdateMode属性设为Conditional。

5. 放一个Timer控件至UpdatePanel1控件中,命名为Timer1,设定Interval属性为2000,也就是每两秒做一次Async-Postback动作。

6. 设定Timer1控件的Enabled属性为False。

7. 放一个Button控件至UpdatePanel1控件中,命名为Button1,Text设为Run。

8. 放一个Label控件至UpdatePanel1控件中,命名为Label1,Text属性设为空白。

9. 放一个Button控件至UpdatePanel1控件中,命名为Button2,Text设为Cancel。

10. 在Timer1控件的Click事件中键入程序3-10中Timer1_Tick函数内的代码。

11. 在Button1控件的Click事件中键入程序3-10内Button1_Click函数中的代码。

12. 在Button2控件的Click事件中键入程序3-10内Button2_Click函数中的代码。

程序3-10

    Samples\3\AjaxDemo1\AsyncPostbackProgressReport.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;
    using System.Threading;
    public partial class AsyncPostbackProgressReport : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
        }
        protected void Timer1_Tick(object sender, EventArgs e)
        {
            //由Cache中取出进度信息
            TaskInformation info = Cache[TaskID_Hidden.Value] as TaskInformation;
            if (info != null)
                Label1.Text = string.Format("Processing {0}%", info.CurrentProgress);
            else
            {
                Label1.Text = string.Empty;
                Button1.Enabled = true;
                Timer1.Enabled = false;
                Button2.Visible = false;
            }
        }
        protected void Button1_Click(object sender, EventArgs e)
        {
            Thread th = new Thread(new ParameterizedThreadStart(Work));
            //产生一个GUID,放到Hidden Field中,作为取得回报进度信息的键值
            TaskID_Hidden.Value = Guid.NewGuid().ToString();
            //创建TaskInformation对象,并将其放入Cache中。
            TaskInformation info = new TaskInformation();
            info.CurrentProgress = 0;
            Cache[TaskID_Hidden.Value] = info;
            th.IsBackground = true;
            Timer1.Enabled = true;
            Button1.Enabled = false;
            Button2.Visible = true;
            Label1.Text = "Processing";
            th.Start(TaskID_Hidden.Value);
        }
        private void Work(object state)
        {
            for (int i = 0; i < 10; i++)
            {
                System.Threading.Thread.Sleep(2000);
                //定时更新Cache中的进度信息对象,如果Cacnel被设为True,那么立即停止工作
                TaskInformation info = Cache[(string)state] as TaskInformation;
                if (info.Cacnel)
                      break;
                info.CurrentProgress = i * 10;
                Cache[(string)state] = info;
            }
            //工作完成后,移除Cache中的进度信息对象
            Cache.Remove((string)state);
        }
        protected void Button2_Click(object sender, EventArgs e)
        {
            //取消时,将Cache中的TaskInformation之Cancel设为True。
            TaskInformation info = Cache[TaskID_Hidden.Value] as TaskInformation;
            info.Cacnel = true;
            Button2.Visible = false;
            Label1.Text = "Canceling....";
        }
    }
    [Serializable]
    public class TaskInformation
    {
        public int CurrentProgress;
        public bool Cacnel;
    }

图3-9是运行时期的画面。

true

图3-9

关于进度回报

使用Timer控件实现进度回报并不算是个好主意,因为它会一直运行Async-Postback动作,每次都会送出整个页面的ViewState到Server端,这是相当低效率的手法,后面的章节会采用PageMethods/Web Servcies等较高效率的手法完成同样的工作。