一个好的进度条能有效的改善用户体验。当然,前提是进度条做得好才行,做得差作用就正好相反了,可能造成程序假死、无法关闭等。
下面是一个真实例子,我自己写代码的经验(由于我使用进度条都是自己琢磨,所以和网上的例子不太一样)。
以下是我一年前写的进度条,当时刚学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 任务取消中
以上是我自己想的一个进度条方案,如有雷同,纯属巧合,欢迎大家指正。