一个好的进度条能有效的改善用户体验。当然,前提是进度条做得好才行,做得差作用就正好相反了,可能造成程序假死、无法关闭等。
下面是一个真实例子,我自己写代码的经验(由于我使用进度条都是自己琢磨,所以和网上的例子不太一样)。
以下是我一年前写的进度条,当时刚学C#,所以写出的进度条窗体效果非常差,正好做一个反例。
public int current &#61; 0;//当前值public int max &#61; 100;//最大值public ProgressForm(){InitializeComponent();}//循环跑进度条public void pand(){int i, old &#61; 0;do{if (current !&#61; old){for (i &#61; 0; i < 10; i&#43;&#43;){this.progressBar1.Value &#43;&#61; 1;this.progressBar1.Update();Application.DoEvents();}old &#61; current;}this.progressBar1.Update();Application.DoEvents();if (current &#61;&#61; 0){this.label1.Text &#61; "正在写入论文信息&#xff0c;请稍等...";}else{this.label1.Text &#61; "正在写入论文正文&#xff0c;请稍等...";}}while (current !&#61; max);while (this.progressBar1.Value !&#61; this.progressBar1.Maximum){this.progressBar1.Value &#43;&#61; 1;this.progressBar1.Update();Application.DoEvents();}}//窗体显示时的事件private void ProgressForm_Shown(object sender, EventArgs e){this.progressBar1.Maximum &#61; max * 10;this.progressBar1.Value &#61; 0;pand();this.Close();}
好吧&#xff0c;代码非常繁琐&#xff0c;我稍微解释一下这段代码以及当时我的想法&#xff1a;
我定义了两个公共变量&#xff1a;max存后台需要完成任务任务数&#xff0c;current存当前完成了第几项任务。然后pand()方法负责循环检测current的值&#xff0c;然后给进度条增加值&#xff0c;当current等于max时停止循环关闭进度条窗体&#xff0c;代表任务完成。
while (this.progressBar1.Value !&#61; this.progressBar1.Maximum){this.progressBar1.Value &#43;&#61; 1;this.progressBar1.Update();Application.DoEvents();}
这段代码的作用是让进度条跑的时候不跳格&#xff0c;类似this.progressBar1.PerformStep();方法&#xff08;当时我并不知道进度条有这个方法&#xff09;。下面说一下这段代码存在的问题&#xff0c;首先使用无限循环会造成程序假死现象&#xff08;就是跑进度的时候无法关闭进度条窗体&#xff09;&#xff0c;这非常影响用户体验&#xff0c;其次是代码繁琐&#xff0c;明明是非常简单的事&#xff0c;写得那么繁琐&#xff0c;既影响阅读又影响性能&#xff0c;最后是进度条的画面和Value不同步&#xff08;即每次进度条的Value已经加完了&#xff0c;进度条还没跑完&#xff09;&#xff0c;导致用户看到的效果是进度还没跑完&#xff0c;进度条就关了。
下面看一下我修改后的进度条窗体代码&#xff1a;
private int _current &#61; 0;/// /// 当前值/// public int Current{get { return _current; }set{_current &#61; value;AddValue();}}private int _max &#61; 100;/// /// 最大值/// public int Max{get { return _max; }set { _max &#61; value;this.progressBar1.Maximum &#61; (_max - 1) * 10;this.progressBar1.Value &#61; 0;}}MainForm father;public ProgressForm(MainForm main){father &#61; main;InitializeComponent();this.label1.Text &#61; "正在写入论文信息&#xff0c;请稍等...";}/// /// 给进度条加值的方法/// private void AddValue(){if (_current > 0){this.label1.Text &#61; "正在写入论文正文&#xff0c;请稍等...";}this.progressBar1.PerformStep();if (this._current * 10 > this.progressBar1.Maximum){this.DialogResult &#61; System.Windows.Forms.DialogResult.OK;this.Close();}}/// /// 取消按钮的方法/// private void Cancel(){father.CreateThesisIsRuning &#61; false;progressBar1.Style &#61; ProgressBarStyle.Marquee;label1.Text &#61; "取消中&#xff0c;请稍等...";while (true){if (!father.CreateThesisTh.IsAlive)break;Application.DoEvents();}}/// /// 取消按钮事件/// /// /// private void buttonX1_Click(object sender, EventArgs e){Cancel();}
同样是需要当前值和最大值变量&#xff0c;不同的是我把这两个字段封装起来了&#xff0c;AddValue()方法负责给进度条增加值&#xff0c;Cancel()方法负责执行用户点击取消按钮的操作。
在设置Current值时执行AddValue()方法&#xff0c;AddValue()方法中使用了this.progressBar1.PerformStep()&#xff0c;这个方法的作用是按照设置好的this.progressBar1.Step值给进度条增加值&#xff0c;比如Step&#61;10&#xff0c;调用这个方法就相当于给进度条添加10次值&#xff0c;每次增加1&#xff0c;就不会造成进度条跳跃格数过大&#xff0c;提升用户体验。
用户在点击取消按钮时&#xff0c;就设置后台进程CreateThesisIsRuning&#61;false&#xff0c;使进程停止&#xff0c;由于进程正常可能停止需要一定的时间&#xff0c;所以用一个循环去检测CreateThesisTh.IsAlive的值判断进程是否结束&#xff0c;此时还要修改进度条类型为ProgressBarStyle.Marquee&#xff0c;Application.DoEvents()可以避免循环卡死。
然后针对进度不同步&#xff0c;在设置Max值得时候&#xff0c;this.progressBar1.Maximum &#61; (_max - 1) * 10&#xff0c;将进度条的最大值设置成max最大值-10&#xff0c;然后最后判断进度条是否结束时使用this._current * 10 > this.progressBar1.Maximum去判断&#xff0c;这样就可以使进度条画面&#xff0c;原理是进度条画面更新比较慢&#xff0c;所以减小进度条的最大值&#xff0c;让进度条先跑完再执行完任务&#xff0c;造成进度条在等任务&#xff0c;虽然还是不同步&#xff0c;但是在用户体验上效果好多了。
下面看一下效果&#xff1a;
图1 任务进行中
图2 任务取消中
以上是我自己想的一个进度条方案&#xff0c;如有雷同&#xff0c;纯属巧合&#xff0c;欢迎大家指正。