学习笔记01
公司前辈让我用C#winform结构来逐步实现一个他曾经写过的配置程序的批处理文件的所有功能。设计什么的就不说了,就是俩字简洁。
直接上过程。

小助手主页面

取消了最大化功能的小助手,如下。
其主要功能简单概括为对一批文件进行复制操作,一次配置过程大约耗时3~5分钟,为了能让使用者感受到程序的工作变化我决定加入进度条来显示。

右键项目-》添加-》窗体-》ConfigByProBarForm.cs
点击‘电脑一键配置’按钮就会跳转到ConfigByProBarForm
并在按钮中加入代码

private void button1_Click(object sender, EventArgs e){ConfigByProBarForm proBarForm = new ConfigByProBarForm();proBarForm.Show();}

进度条

进度条的实现有两类,一种假一种真。
假进度条指让进度条进度随我们的想法直接变化,而不考虑实际程序工作过程,程序结束而后进度条直接满值进而结束。
真进度条则在每一步我们定义好的检查点进度增加,进而告诉使用者程序的工作进度。

我选择做一个真的并且一弹窗形式展现。
这里主要参考https://blog.csdn.net/feiyang5260/article/details/90272311中的方法四。

进度条页面主要设计

加入控件如下

这里说一下一开始我照抄(新手都这样QAQ)前面博客里的方法四,但是奈何那方法没写清楚并且我怎么也显示不成功,于是痛定思痛,我仔细反省了一下。
既然BackgroundWorker控件是异步线程实现进度条,虽然我同原作者一样将其加入主页面进度条放在弹出页面中,但无法实现,技术薄弱排查不出原因,因此干脆将控件都放在弹窗中,弹窗加载时就启动它,这样一来应该不影响使用。试了一下成功。
这里注意将后台任务控件的两个属性设置为true
它俩字面意思就是允许报告进度和支持取消操作

子窗体代码实现

public ConfigByProBarForm(){InitializeComponent();/*以下为参考MSDN中BackgroundWorker使用方式写出的,和原博客对比它没有加入对DoWork的引用并且主窗体和子窗体都写了一个RunWorkerCompleted,但只引用了子窗体的,这可能是代码运行失败的原因?若各位看客能看懂其实现原理烦请告知。*///模拟完成程序功能的代码backgroundWorker1.DoWork += BackgroundWorker_DoWork;//绑定进度条改变事件backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;//绑定后台操作完成、取消、异常的事件backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;}private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e){BackgroundWorker worker = sender as BackgroundWorker;for (int i = 1; i <= 100; i++){//判断是否取消if (worker.CancellationPending){e.Cancel = true;break;}else{Thread.Sleep(100);//报告进度worker.ReportProgress(i);}}//throw new NotImplementedException();}private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e){progressBar1.Value = e.ProgressPercentage;//获取异步任务的进度条label1.Text = e.ProgressPercentage.ToString();}private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e){//这里弹窗提示还是按钮变化凭各位喜好if (e.Error != null){MessageBox.Show(e.Error.Message);}else if (e.Cancelled){MessageBox.Show("It's Cancelled!");}else{//MessageBox.Show("Completed!");button1.Text = "完成!";}}//取消按钮private void button1_Click(object sender, EventArgs e){/*一开始认为进度值达到100才能算完成,不到则必定没完成,后来调试时发现有时候进度值可能没到100但是任务做完了,此时条件a就显得不够合理,因此改为检测按钮状态,也算是增加了程序的健壮性吧,哈哈。*///if(progressBar1.Value != 100)      --------条件aif(button1.Text == "取消"){//请求取消挂起的后台操作backgroundWorker1.CancelAsync();button1.Enabled = false;Close();}else{Close();}}//加载子窗体时就开始执行后台任务进程private void ProgressForm_Load(object sender, EventArgs e){backgroundWorker1.RunWorkerAsync();}}

运行效果

实际体验

我是先写好这个进度条的程序后,才把之前写好功能的源程序主要功能代码移植过来,美滋滋的想着实现功能。结果不出意料的出现各种问题…

先看一下原代码的DoWork

private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e){BackgroundWorker worker = sender as BackgroundWorker;bool monitor = false;//监视变量int i = 1;int num = 24;//任务数量while (!monitor){//判断是否取消if (worker.CancellationPending){e.Cancel = true;break;}else if (i == num + 1){monitor = true;}else{#region 任务1//...worker.ReportProgress(i * 100 / num);i++;#endregion......#region 任务24//...worker.ReportProgress(i * 100 / num);i++;#endregion}}}

之前写实验程序的时候使用的是 循环睡眠线程 来模拟任务的耗时操作,但是实际任务不可能每个都一样,我又要写一个真进度条,因此我分割了任务并且每执行一部分就报告一次进度,我又想当然的外套了一层while循环来检测取消和任务完成状态。
紧接着出现问题。

首先是 取消失效了,因为任务没执行结束,所以显然不会回头执行取消的判断部分。
其次我参考的几乎所有博客都使用循环来模拟任务进度,以及最开始子窗体建立过程中 “对象.方法 += 方法”的写法让我猜测…_DoWork,…_ReportProgress(…),…RunWorkerCompleted这三个方法在运行过程中被执行了几次。

换个思路重新调试

我决定重写DoWork的实现,并拆开模拟任务的循环来观察DoWork的执行过程,发现DoWork只被执行了一次,另外两个应该是同理并且是多线程一直监视我们放入的对象。这个想法启发了我,取消能不能也安排一个多线程来监视它呢?这样我就不用每个任务后面都加入判断了,那显然太蠢。

//在DoWork 的worker对象赋值之后加入Thread(ParameterizedThreadStart)的引用实现多线程,就一个程序
//也就不用考虑线程安全的问题了
//Thread cancel = new Thread(BackgroundWorker_Cancelled);//同是新定义一个报告进度并可以使其取消的方法,加入任务数量参数count和任务标记数i,i自增来标记进度
private static void BackgroundWorker_ReportProgress(object worker, DoWorkEventArgs e, int count,int i){//BackgroundWorker worker1 = (BackgroundWorker)worker;//判断是否取消if (worker1.CancellationPending){e.Cancel = true;}//报告进度worker1.ReportProgress(100 / count * i);i++;}

此时我的DoWork代码如下

private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e){BackgroundWorker worker = sender as BackgroundWorker;///注意使用 Thread(ParameterizedThreadStart)构造方法的线程参数必须为object对象,且这种方式线程不安全Thread cancel = new Thread(BackgroundWorker_Cancelled);cancel.Start(worker);int count = 5;int i = 1;Thread.Sleep(100);BackgroundWorker_ReportProgress(worker, e, count,i);Thread.Sleep(100);BackgroundWorker_ReportProgress(worker, e, count,i);Thread.Sleep(100);BackgroundWorker_ReportProgress(worker, e, count,i);Thread.Sleep(100);BackgroundWorker_ReportProgress(worker, e, count,i);Thread.Sleep(100);BackgroundWorker_ReportProgress(worker, e, count,i);}

老手显然能看出i的变量作用域在变化所以i的自增实现是失败的,并且Thread(ParameterizedThreadStart)构造方法的线程参数必须为object对象,且这种方式线程不安全
回头接着改报告方法传入的BackgroundWorker对象为object,新定义了一个BackgroundWorker然后接着调试,发现新的worker1对象默认的进度值为100,运行就会报错超出进度条的最大值。加个线程的方法被我放弃,又换了一个思路。
!========================================!

我决定仍然使用一开始就定义好的BackgroundWorker对象,并且静态化任务标记数 i,使其成为类属性。并且为了避免当任务数无法被100整除导致最终进度不满100就直接完成的尴尬状况(我将count调成7就出现了进度值98而任务完成的情况,且点击完成并不会关闭窗口,这也是我修改取消按钮判断状况的原因)。
将count定义为float,
代码如下

static int i = 1;
private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e){BackgroundWorker worker = sender as BackgroundWorker;float count = 7;//多次重复模拟不同的任务处理Thread.Sleep(100);BackgroundWorker_ReportProgress(worker, e, count);Thread.Sleep(100);BackgroundWorker_ReportProgress(worker, e, count);Thread.Sleep(100);BackgroundWorker_ReportProgress(worker, e, count);Thread.Sleep(100);BackgroundWorker_ReportProgress(worker, e, count);Thread.Sleep(100);BackgroundWorker_ReportProgress(worker, e, count);Thread.Sleep(100);BackgroundWorker_ReportProgress(worker, e, count);Thread.Sleep(100);BackgroundWorker_ReportProgress(worker, e, count);}private static void BackgroundWorker_ReportProgress(BackgroundWorker worker,DoWorkEventArgs e, float count){//判断是否取消if (worker.CancellationPending){e.Cancel = true;}//报告进度worker.ReportProgress((int)(100 / count * i));i++;}

此时再调试效果如下

总算成功了。
简单的进度条让我踩了很多坑但最终还是完成了它,决定写个博客记录一下学习过程,第一次写,如有误烦请告知。

====================================================================
2021-1-18 更新
拖了几天终于想起这个博客,更新一下。
上面写的进度条在让我检查的时候发现了很多bug。
在我放弃使用循环采用完成不同任务并在每次任务后打标记进而增加进度的方式后,我选择了将判断取消的部分放在了报告进度变化的方法中,上述最近的代码块中就是。
在我检测的时候发现,取消是个假的。
当我把所有模拟任务耗时的操作Thread.Sleep(100);
改成了
MessageBox.Show(string.Format(“任务{0},i”));
问题就出现了,在我点击取消后,进度条窗口关闭,而任务进度弹窗依旧出现。

 MessageBox.Show(string.Format("任务{0}", i));BackgroundWorker_ReportProgress(worker, e, count);MessageBox.Show(string.Format("任务{0},{1}", i,backgroundWorker1.CancellationPending));BackgroundWorker_ReportProgress(worker, e, count);


此时点击取消,
可以看到进度条弹窗关闭,在确定任务1,然后任务二依旧弹出
同时我加入了对backgroundWorker1.CancellationPending值的检测,要知道在前面代码的设计中,当点击取消后执行backgroundWorker1.CancelAsync()操作,此时backgroundWorker1.CancellationPending的值会从false改为true,进而取消。
然而结果是我自定义的BackgroundWorker_ReportProgress方法形同虚设…
后来查了很久,主要是因为BackgroundWorker进程没有关闭,所以会一直运行,我尝试过关闭线程的Abort方法,会直接报错并且退出整个程序。最终我还是选择了每个任务都加入if判断,若CancellationPending值为真则不执行任务代码,这样完成了真正意义的取消。
然后这引发了我的思考。
1.有取消是否也应该有暂停继续呢?我还没腾出时间来实现这些。
2.一开始我选择把判断取消的代码放在自定义方法里是为了减少代码复用,然而这样我的方法就变得没有意义,依旧有大量复用,那么我设计真实进度的意义在哪里呢?此时想起假进度条的事,又觉得前辈们做的可能没错。任务完成就行,进度可能并不重要。

1.[C# Winform]BackgroundWorker实现进度条的那点事儿相关推荐

  1. Winform中使用进度条的一个例子

    因为在winform程序中,UI线程负责界面相关的工作. Winform中使用进度条的方法有很多,可以直接创建一个新的线程Thread执行耗时的方法:也可以使用线程池. 例如: //使用线程池异步执行 ...

  2. WinForm中异步加载数据并使用进度条

    在WinForm程序中,有时会因为加载大量数据导致UI界面假死,这种情况对于用户来说是非常不友好的.因此,在加载大量数据的情况下,首先应该将数据加载放在另一线程中进行,这样保证了UI界面的响应:其次可 ...

  3. Winform中封装DevExpress的MarqueeProgressBarComtrol实现弹窗式进度条效果

    场景 在Winform中实现弹窗式进度条 新建一个窗体,然后在窗体中加入进度条控件,然后在触发进度条的事件中将加载进度报告给进度条控件. 注: 博客主页: https://blog.csdn.net/ ...

  4. WinForm:进度条的实现(异步)

    在WinForm中经常遇到一些费时的操作界面,比如统计某个磁盘分区的文件夹或者文件数目,如果分区很大或者文件过多的话,处理不好就会造成"假死"的情况,或者报"线程间操作无 ...

  5. 关于C# WinForm中进度条的实现方法

    http://www.cnblogs.com/Sue_/articles/2024932.html 进度条是一个软件人性化考虑之一,他给用户的感觉就是程序内部在不停的动作,执行到了什么程度,而不是整个 ...

  6. C#遍历文件读取Word内容以及使用BackgroundWorker对象打造平滑进度条

    本文将给出一个实例用于介绍如何利用C#遍历目录中的文件并打印Word文件中的内容,同时在界面上利用一个相对平滑的进度条来显示文件处理的情况.下面是程序运行时的截图: 下面来看看程序的具体实现步骤. 首 ...

  7. winform进度条实现

    最近要给一个 Winform 项目添加功能,需要一个能显示进度条的弹窗,还要求能够中止任务,所以就做了一个,在此做个记录总结.虽然用的是比较老的 Winform 技术,不过其中的原理都是相通的. lo ...

  8. Winform 进度条弹窗和任务控制

    Winform 进度条弹窗和任务控制 目录 Winform 进度条弹窗和任务控制 一.弹窗前台 二.弹窗后台 三.使用方法 四.效果展示和代码地址 独立观察员 2020 年 11 月 17 日 最近要 ...

  9. winform进度条的实现

    1.第一种方法:直接实现 (1)界面 (2)设计一个按钮事件 private void button1_Click(object sender, EventArgs e){progressBar1.V ...

最新文章

  1. MATLAB实现微积分基础知识(求导,积分,插值,曲线拟合,最小二乘)
  2. 电商企业纷纷结缘信息化 管理系统如何给力?
  3. 【Groovy】构建工具 ( 构建工具引入 | Gradle 构建工具作用 | 传统的依赖管理 )
  4. PCA主成分分析以及Python实现(阅读笔记)
  5. Raspberry Pi 2 Model B Pi4J 示例
  6. JavaScript实现表单的分向提交
  7. 14 | 排序优化:如何实现一个通用的、高性能的排序函数?
  8. c语言的单行注释范围,c语言中的注释,multi-line comment
  9. matlab dotchart,MATLAB中如何用对数方式显示图形坐标?
  10. SpringCloud学习笔记026---SpringBoot中使用不同类型的数据库_MySql_PostGreSql_使用template
  11. WORD批量更改所有图片大小
  12. Vagrant (一) - 基本知识
  13. android黑色半透明dialog背景,Dialog背景半透明
  14. 单点登录系统设计分析
  15. MySQL 8.0.19安装教程(windows 64位)
  16. Excel 数据透视表教程大全之 07 数据透视表使用日期字段自动按月、年对销售数据进行分组(教程含数据)
  17. 如何让微信官方给你加圣诞帽
  18. [原创]WIA 学习笔记
  19. 福建福州软考考点安排在哪?
  20. 鸿蒙os手机评测视频,鸿蒙OS正式发布:荣耀智慧屏首发,一起来体验鸿蒙OS吧

热门文章

  1. Java反编译工具Luyten介绍
  2. CentOS7 搭建php环境
  3. Android Studio 2.0+Gradle 2.12编译Oculus Mobile SDK
  4. 中国计算机软件行业分析3--软件倾销
  5. 【历史上的今天】2 月 10 日:QQ 诞生;IBM 电脑击败人类象棋冠军;谷歌光纤发布
  6. 更改office安装的默认路径
  7. java 奇数中文乱码_java web 乱码 整理
  8. PADS 9.5 acs文件导入失败
  9. ios webview html交互 卡死,iOS 之webview 的js交互(alert、confirm、prompt)弹窗造成界面卡死...
  10. java 接口校验接收参数_java接口参数校验