在.NET WinForm程序中,大多数情况下我们是知道程序运行所需要的时间或步骤的,比如批量复制文件时文件的数量,数据导出或导入时数据的总行数等等。对于步骤比较确定的操作,如果程序执行过程时间较长,很容易使用BackgroundWorker结合ProgressBar来显示一个实时的进度。相关内容大家可以看我博客中的其它文章,有关如何使用BackgroundWorker和ProgressBar。但是,有的时候我们是不确定程序执行的具体步骤或时长的,比如连接一个远程服务或数据库服务,或者调用一个远程过程或WebService等,这个时候我们就没有办法去触发BackgroundWorker的ProgressChanged事件,因此也就不能实时去更新ProgressBar的进度了。有两种替代的办法可以解决这个问题。
第一是将ProgressBar的Style设置为Marquee而不是默认的Blocks。在Marquee模式下,进度条会不停地向前走用来模拟一个长时间的操作。事实上,Windows中也有很多类似的进度条,大多都是出现在对操作过程所需的步骤和时长不太确定的时候。这种方法很简单,不过你仍然要将后台的执行过程放到多线程来执行,否则进度条会卡在UI线程中。一个好的办法就是依旧使用BackgroundWorker组建,将后台的执行程序放到BackgroundWorker的DoWorker事件中,然后调用BackgroundWorker的RunWorkerAsync方法来异步执行程序。这样,UI线程和后台执行程序的线程可以分开,进度条便不会再卡了。
第二种方法是使用System.Windows.Forms.Timer定时器控件,设置好Timer的Interval间隔时间,在Timer的Tick事件中来更新ProgressBar的进度。由于Timer天生就是多线程的,所以这种办法实现起来很方便。
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 using System.Threading; 10 11 namespace WindowsFormsApplication2 12 { 13 public partial class Form1 : Form 14 { 15 private BackgroundWorker worker = new BackgroundWorker(); 16 private System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer(); 17 public Form1() 18 { 19 InitializeComponent(); 20 this .progressBar1.Value = 0 ; 21 this .progressBar1.Maximum = 200 ; 22 this .progressBar1.Step = 1 ; 23 timer.Interval = 100 ; 24 timer.Tick += new EventHandler(timer_Tick); 25 worker.WorkerReportsProgress = true ; 26 worker.DoWork += new DoWorkEventHandler(worker_DoWork); 27 worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(worker_RunWorkerCompleted); 28 worker.RunWorkerAsync(); 29 timer.Start(); 30 } 31 32 void timer_Tick( object sender, EventArgs e) 33 { 34 if ( this .progressBar1.Value < this .progressBar1.Maximum) 35 { 36 this .progressBar1.PerformStep(); 37 } 38 } 39 40 void worker_RunWorkerCompleted( object sender, RunWorkerCompletedEventArgs e) 41 { 42 timer.Stop(); 43 this .progressBar1.Value = this .progressBar1.Maximum; 44 MessageBox.Show( " Complete! " ); 45 } 46 47 void worker_DoWork( object sender, DoWorkEventArgs e) 48 { 49 int count = 100 ; 50 for ( int i = 0 ; i < count; i ++ ) 51 { 52 Thread.Sleep( 100 ); 53 } 54 } 55 } 56 } Timer每隔100毫秒便调用一次Tick事件,在该事件中更新ProgressBar的当前进度。注意需要判断ProgressBar的Value必须小于Maximum值时才去执行Performance()方法,否则会出现ProgressBar的Value大于Maximum的值而抛异常。根据BackgroundWorker的DoWork方法执行所需的时间长短不同,ProgressBar的进度可能会在BackgroundWorker执行具体操作完成之前到达100%,也可以没有到达100%,所以在BackgroundWorker的RunWorkerCompleted事件中将ProgressBar的进度更新为100%,以确保进度在最后是一个完成的状态。
如果你在应用程序中确实需要使用进度条来提示用户后台程序在完成一个耗时较长的操作,而且你还希望进度条能尽量模拟出程序执行的步骤,而不是一个Marquee状态,可以考虑使用Timer定时器控件。因为,有的时候我们确实很难评估一个执行过程到底需要多少步骤或者需要多长时间才能完成。