C#Winform進度條的使用
一個好的進度條能有效的改善使用者體驗。當然,前提是進度條做得好才行,做得差作用就正好相反了,可能造成程式假死、無法關閉等。
下面是一個真例項子,我自己寫程式碼的經驗(由於我使用進度條都是自己琢磨,所以和網上的例子不太一樣)。
以下是我一年前寫的進度條,當時剛學C#,所以寫出的進度條窗體效果非常差,正好做一個反例。
public int current = 0;//當前值 public int max = 100;//最大值 public ProgressForm() { InitializeComponent(); } //迴圈跑進度條 public void pand() { int i, old = 0; do { if (current != old) { for (i = 0; i < 10; i++) { this.progressBar1.Value += 1; this.progressBar1.Update(); Application.DoEvents(); } old = current; } this.progressBar1.Update(); Application.DoEvents(); if (current == 0) { this.label1.Text = "正在寫入論文資訊,請稍等..."; } else { this.label1.Text = "正在寫入論文正文,請稍等..."; } } while (current != max); while (this.progressBar1.Value != this.progressBar1.Maximum) { this.progressBar1.Value += 1; this.progressBar1.Update(); Application.DoEvents(); } } //窗體顯示時的事件 private void ProgressForm_Shown(object sender, EventArgs e) { this.progressBar1.Maximum = max * 10; this.progressBar1.Value = 0; pand(); this.Close(); }
好吧,程式碼非常繁瑣,我稍微解釋一下這段程式碼以及當時我的想法:
我定義了兩個公共變數:max存後臺需要完成任務任務數,current存當前完成了第幾項任務。然後pand()方法負責迴圈檢測current的值,然後給進度條增加值,當current等於max時停止迴圈關閉進度條窗體,代表任務完成。
while (this.progressBar1.Value != this.progressBar1.Maximum) { this.progressBar1.Value += 1; this.progressBar1.Update(); Application.DoEvents(); }
這段程式碼的作用是讓進度條跑的時候不跳格,類似this.progressBar1.PerformStep();方法(當時我並不知道進度條有這個方法)。下面說一下這段程式碼存在的問題,首先使用無限迴圈會造成程式假死現象(就是跑進度的時候無法關閉進度條窗體),這非常影響使用者體驗,其次是程式碼繁瑣,明明是非常簡單的事,寫得那麼繁瑣,既影響閱讀又影響效能,最後是進度條的畫面和Value不同步(即每次進度條的Value已經加完了,進度條還沒跑完),導致使用者看到的效果是進度還沒跑完,進度條就關了。
下面看一下我修改後的進度條窗體程式碼:
private int _current = 0; /// <summary> /// 當前值 /// </summary> public int Current { get { return _current; } set { _current = value; AddValue(); } } private int _max = 100; /// <summary> /// 最大值 /// </summary> public int Max { get { return _max; } set { _max = value; this.progressBar1.Maximum = (_max - 1) * 10; this.progressBar1.Value = 0; } } MainForm father; public ProgressForm(MainForm main) { father = main; InitializeComponent(); this.label1.Text = "正在寫入論文資訊,請稍等..."; } /// <summary> /// 給進度條加值的方法 /// </summary> private void AddValue() { if (_current > 0) { this.label1.Text = "正在寫入論文正文,請稍等..."; } this.progressBar1.PerformStep(); if (this._current * 10 > this.progressBar1.Maximum) { this.DialogResult = System.Windows.Forms.DialogResult.OK; this.Close(); } } /// <summary> /// 取消按鈕的方法 /// </summary> private void Cancel() { father.CreateThesisIsRuning = false; progressBar1.Style = ProgressBarStyle.Marquee; label1.Text = "取消中,請稍等..."; while (true) { if (!father.CreateThesisTh.IsAlive) break; Application.DoEvents(); } } /// <summary> /// 取消按鈕事件 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void buttonX1_Click(object sender, EventArgs e) { Cancel(); }
同樣是需要當前值和最大值變數,不同的是我把這兩個欄位封裝起來了,AddValue()方法負責給進度條增加值,Cancel()方法負責執行使用者點選取消按鈕的操作。
在設定Current值時執行AddValue()方法,AddValue()方法中使用了this.progressBar1.PerformStep(),這個方法的作用是按照設定好的this.progressBar1.Step值給進度條增加值,比如Step=10,呼叫這個方法就相當於給進度條新增10次值,每次增加1,就不會造成進度條跳躍格數過大,提升使用者體驗。
使用者在點選取消按鈕時,就設定後臺程序CreateThesisIsRuning=false,使程序停止,由於程序正常可能停止需要一定的時間,所以用一個迴圈去檢測CreateThesisTh.IsAlive的值判斷程序是否結束,此時還要修改進度條型別為ProgressBarStyle.Marquee,Application.DoEvents()可以避免迴圈卡死。
然後針對進度不同步,在設定Max值得時候,this.progressBar1.Maximum = (_max - 1) * 10,將進度條的最大值設定成max最大值-10,然後最後判斷進度條是否結束時使用this._current * 10 > this.progressBar1.Maximum去判斷,這樣就可以使進度條畫面,原理是進度條畫面更新比較慢,所以減小進度條的最大值,讓進度條先跑完再執行完任務,造成進度條在等任務,雖然還是不同步,但是在使用者體驗上效果好多了。
下面看一下效果:
圖1 任務進行中
圖2 任務取消中
以上是我自己想的一個進度條方案,如有雷同,純屬巧合,歡迎大家指正。