進度條是一個軟體人性化考慮之一,他給使用者的感覺就是程式內部在不停的動作,執行到了什麼程度,而不是整個介面僵死,以至於使用者不知道程式在做什麼!
看了好幾個WinForm程式了,發現他們對進度條的處理完全失去了進度條的作用。他們都是採用Timer來處理,線上程結束的時候,直接賦值進度條達到100%。和我以前做WebForm程式的時候完全不一樣,做WebForm程式的時候,進度條是根據總體資料和每步執行後而計算和更新的。在看了這幾個WinForm程式後,我在想:是否所有WinForm程式,在進度條的處理上都不能保證即時進度顯示?
其實用Timer來處理,不停的更新進度條只是程式作者偷懶的方法。當然這樣的好處就是可以簡單化處理進度條,代碼量少,不易出錯,調試方便。
還有一種方法,就是可以及時更新進度條的資料的。那就是採用事件驅動機制,在子線程中監視複雜處理過程中的設定的事件,及時更新!直接看代碼:
using System;using System.ComponentModel;using System.Windows.Forms;namespace WindowsApplication1{ /// <summary> /// Form1 類 /// </summary> public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { //用子線程工作 new System.Threading.Thread(new System.Threading.ThreadStart(StartDownload)).Start(); } //開始下載 public void StartDownload() { Downloader downloader = new Downloader(); downloader.onDownLoadProgress += new Downloader.dDownloadProgress(downloader_onDownLoadProgress); downloader.Start(); } //同步更新UI void downloader_onDownLoadProgress(long total, long current) { if (this.InvokeRequired) { this.Invoke(new Downloader.dDownloadProgress(downloader_onDownLoadProgress), new object[] { total, current }); } else { this.progressBar1.Maximum = (int)total; this.progressBar1.Value = (int)current; } } } /// <summary> /// 下載類(您的複雜處理類) /// </summary> public class Downloader { //委託 public delegate void dDownloadProgress(long total,long current); //事件 public event dDownloadProgress onDownLoadProgress; //開始類比工作 public void Start() { for (int i = 0; i < 100; i++) { if (onDownLoadProgress != null) onDownLoadProgress(100, i); System.Threading.Thread.Sleep(100); } } }}
上面的文章是在一個部落格上看到的,覺得還是可行的,還沒試過,另外也在CSDN的論壇上看到過一個回複,內容如下:
關於查詢統計過程顯示等待提示框的問題!!!
1 樓hbxtlhx(平民百姓-自已動手,豐衣足食) 回複於 2006-12-30 11:45:46 得分 70
像這種問題要用非同步來處理,在非同步處理過程中Timer是可以執行的,當非同步執行完成了把Timer關閉等等
因為你的程式是同步的在查詢統計沒有完成前程式的其它線程都處理於等待這個查詢統計完成後才執行,因此你的Timer也是在等查詢統計完成後才執行就不能達到你要的效果了.Top
2 樓hbxtlhx(平民百姓-自已動手,豐衣足食) 回複於 2006-12-30 11:51:01 得分 0
例如如下的代碼就是用非同步來執行的:
delegate object dlExecuteQuery();private void button1_Click(object sender, EventArgs e){dlExecuteQuery de=new dlExecuteQuery(this.Query());IAsyncResult ir = de.BeginInvoke(null, null);Form f=new Form()f.ShowDialog(this);Application.DoEvents();while (!ir.IsCompleted){Application.DoEvents();}object obj = de.EndInvoke(ir);f.Close();}private object Query(){//長時間的操作}
另一種方法:轉自:CSDN 寓翁專欄
最近看了好多人問這方面的問題,以前我也寫過一篇blog,裡面說了如何在子線程中控制進度條。但目前大多數環境,需要彈出強制回應視窗,來顯示進度條,那麼只需要在原先的基礎上稍作修改即可。
首先是進度條表單,需要在上面添加進度條,然後去掉ControlBox。除此外,還要增加一個方法,用來控制進度條的增加幅度,具體如下:
/// <summary> /// Increase process bar /// </summary> /// <param name=”nValue”>the value increased</param> /// <returns></returns> public bool Increase( int nValue ) { if( nValue > 0 ) { if( prcBar.Value + nValue < prcBar.Maximum ) { prcBar.Value += nValue; return true; } else { prcBar.Value = prcBar.Maximum; this.Close(); return false; } } return false; }
接著就是主表單了,如何進行操作了,首先需要定義兩個私人成員,一個委託。其中一個私人成員是儲存當前進度條表單對象,另一個是儲存委託方法(即增加進度條尺度),具體如下:
private frmProcessBar myProcessBar = null; private delegate bool IncreaseHandle( int nValue ); private IncreaseHandle myIncrease = null;接著要在主表單中提供函數來開啟進度條表單,如下: /// <summary> /// Open process bar window /// </summary> private void ShowProcessBar() { myProcessBar = new frmProcessBar(); // Init increase event myIncrease = new IncreaseHandle( myProcessBar.Increase ); myProcessBar.ShowDialog(); myProcessBar = null; }
那麼現在就可以開始建立線程來運行,具體如下:
/// <summary> /// Sub thread function /// </summary> private void ThreadFun() { MethodInvoker mi = new MethodInvoker( ShowProcessBar ); this.BeginInvoke( mi ); Thread.Sleep( 1000 );//Sleep a while to show window bool blnIncreased = false; object objReturn = null; do { Thread.Sleep( 50 ); objReturn = this.Invoke( this.myIncrease, new object[]{ 2 } ); blnIncreased = (bool)objReturn ; } while( blnIncreased ); }
注意以上,在開啟進度條表單和增加進度條進度的時候,一個用的是BeginInvoke,一個是Invoke,這裡的區別是BeginInvoke不需要等待方法運行完畢,而Invoke是要等待方法運行完畢。還有一點,此處用傳回值來判斷進度條是否到頭了,如果需要有其他的控制,可以類似前面的方法來進行擴充。
啟動線程,可以如下:
Thread thdSub = new Thread( new ThreadStart( ThreadFun ) ); thdSub.Start();
這樣,一個用模式開啟進度條表單就做完了。