您现在的位置是:首页 > 文章详情

C# 根据BackgroundWorker异步模型和ProgressBar控件,自定义进度条控件

日期:2020-05-30点击:333

C# 根据BackgroundWorker异步模型和ProgressBar控件,自定义进度条控件

前言
程序开发过程中,难免会有的业务逻辑,或者算法之类产生让人能够感知的耗时操作,例如循环中对复杂逻辑处理;获取数据库百万乃至千万级数据;http请求的时候等......
用户在使用UI操作并不知道程序的内部处理,从而误操作导致程序无响应,关闭程序等待影响体验的情况,因此,在等待过程中提供友好的等待提示是有必要的,接下来
我们一起封装一个自定义进度条控件!

主要使用技术(C#相关)
BackgroundWoker异步模型
ProgressBar控件
泛型
定时器 System.Timers.Timer
自定义控件开发
项目解决方案

BackgroundworkerEx : 自定义进度条控件工程
Test : 调用BackgroundworkerEx的工程(只是展示如何调用)
处理控件样式

新建一个ProgressbarEx名称的 用户控件
添加Labal控件(lblTips),用于展示进度条显示的信息状态
添加一个PictureBox控件(PicStop),充当关闭按钮,用于获取用户点击事件,触发关闭/终止进度条
添加进度条ProgressBar控件(MainProgressBar)
处理代码如下:
进度条样式为"不断循环",并且速度为50
该自定义用户控件不展示在任务栏中
图片控件被点击事件------>设置当前属性IsStop=true,指示过程终止;
TipMessage属性,用于设置进度条的信息
SetProgressValue(int value) 设置进度条的Value属性,使得在ProgressBarStyle.Marquee样式中动画平滑
MouseDown/MouseUp/MouseMove这三个事件是用于拖动无边框的用户控件(代码就不贴了)
public ProgressbarEx()
{

InitializeComponent(); MainProgressBar.Style = ProgressBarStyle.Marquee; MainProgressBar.MarqueeAnimationSpeed = 50; this.ShowInTaskbar = false; PicStop.Click += (s, eve) => { IsStop = true; }; this.MouseDown += CusProgressForm_MouseDown; this.MouseUp += CusProgressForm_MouseUp; this.MouseMove += CusProgressForm_MouseMove;

}

///
/// Need Stop ?
///
public bool IsStop { get; private set; } = false;

///
/// TipMessage
///
public string TipMessage { get; set; }

///
/// TipMessage
///
public string TipMessage
{

get { return lblTips.Text; } set { lblTips.Text = value; }

}

///
/// Set ProgressBar value ,which makes ProgressBar smooth
///
///
public void SetProgressValue(int value)
{

if (MainProgressBar.Value == 100) MainProgressBar.Value = 0; MainProgressBar.Value += value; 

}

到现在,这个自定义进度条控件的样式基本完成了.

功能逻辑处理
运行前所需
定义BackgroundWorkerEx泛型类,并且继承于 IDisposable
释放资源;

 /// <summary> /// Dispose /// </summary> public void Dispose() { try { DoWork = null; RunWorkCompleted = null; WorkStoped = null; _mWorkerThread = null; _mWorker.Dispose(); _mWorker = null; _mTimer = null; } catch (Exception){} }

T用与异步处理的时候,传递T类型
因为我们是通过.Net 的 BackgroundWorker异步模型来做的,所以我们理所当然定义相关的事件:
异步开始
异步完成
加上我们自定义扩展的异步停止
......报告进度事件在此进度条样式中并不需要
我们先定义这四个事件所用到的参数,因为在BackgroundWorkerEx泛型类中,我们还是使用BackgroundWorker来处理异步过程,因此我们定义的参数泛型类需要继承原来的参数类型,并且在传输传递中,将原生BackgroundWorker的Argument,Result属性转成全局的泛型T,这样我们在外部调用的时候,拿到的返回结果就是我们传入到BackgroundWorkerEx泛型类中的T类型,而不需要使用as进行转换; 注:因为原生没有停止相关事件,所以自定义异步停止的事件参数使用的是DoWorkEventArgs

public class DoWorkEventArgs<T> : DoWorkEventArgs { public new T Argument { get; set; } public new T Result { get; set; } public DoWorkEventArgs(object argument) : base(argument) { Argument = (T)argument; } } 
public class RunWorkerCompletedEventArgs<T> : RunWorkerCompletedEventArgs { public new T Result { get; set; } public RunWorkerCompletedEventArgs(object result, Exception error, bool cancelled) : base(result, error, cancelled) { Result = (T)result; } }

接着我们需要去定义事件,参数使用以上定义的泛型类

public delegate void DoWorkEventHandler(DoWorkEventArgs<T> Argument); /// <summary> /// StartAsync /// </summary> public event DoWorkEventHandler DoWork; public delegate void StopEventHandler(DoWorkEventArgs<T> Argument); /// <summary> /// StopAsync /// </summary> public event StopEventHandler WorkStoped; public delegate void RunWorkCompletedEventHandler(RunWorkerCompletedEventArgs<T> Argument); /// <summary> /// FinishAsync /// </summary> public event RunWorkCompletedEventHandler RunWorkCompleted;

定义全局的字段
private BackgroundWorker _mWorker = null;异步操作必要;
private T _mWorkArg = default(T);操作传递进来的参数类并且返回到外部
private Timer _mTimer; 定时器检测自定义进度条控件属性IsStop是否为true,并且动态修改进度条消息
private Thread _mWorkerThread = null;异步操作在该线程中,终止时调用About()抛出ThreadAbortException异常,用于标记当前是停止而不是完成状态
private int _miWorkerStartDateSecond = 0; 异步消耗时间(非必要)
private int _miShowProgressCount = 0; 动态显示"."的个数(非必要)
private ProgressbarEx _mfrmProgressForm = null; 自定义进度条控件实例

 /// <summary> /// .Net BackgroundWorker /// </summary> private BackgroundWorker _mWorker = null; /// <summary> /// Whole Para /// </summary> private T _mWorkArg = default(T); /// <summary> /// Timer /// </summary> private Timer _mTimer = null; /// <summary> /// WorkingThread /// </summary> private Thread _mWorkerThread = null; /// <summary> /// Async time sec /// </summary> private int _miWorkerStartDateSecond = 0; /// <summary> /// Async time dot /// </summary> private int _miShowProgressCount = 0; /// <summary> /// ProgressbarEx /// </summary private ProgressbarEx _mfrmProgressForm = null; 

定义全局属性
IsBusy 返回_mWorker的工作忙碌是否
ProgressTip 自定义进度条控件显示内容

/// <summary> /// Express Busy /// </summary> public bool IsBusy { get { if (_mWorker != null) { return _mWorker.IsBusy; } return false; } } /// <summary> /// 进度条提示 默认: 正在加载数据,请稍后[{0}]{1} /// </summary> public string ProgressTip { get; set; } = "Elapsed Time[{0}]{1}";

到现在,我们已经将必要的字段,属性,样式都处理完成!!!  接下来我们就要实现方法

方法实现
异步工作事件,用法与BackgroundWorker一致,
如果调用处没有注册DoWork事件,则直接返回
将接受到的参数创建成泛型参数类
开线程,将异步操作放在该线程中操作,注意设置线程的IsBackground=true,防止主进程意外退出,线程还在处理
循环直到线程结束
e.Result = Argument.Result;将结果赋予Result,在停止或者完成事件中可以获取到结果

 /// <summary> /// Working /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Worker_DoWork(object sender, DoWorkEventArgs e) { if (DoWork == null) { e.Cancel = true; return; } DoWorkEventArgs<T> Argument = new DoWorkEventArgs<T>(e.Argument); try { if (_mWorkerThread != null && _mWorkerThread.IsAlive) { _mWorkerThread.Abort(); } } catch (Exception) { Thread.Sleep(50); } _mWorkerThread = new Thread(a => { try { DoWork?.Invoke(a as DoWorkEventArgs<T>); } catch (Exception) { } }); _mWorkerThread.IsBackground = true; _mWorkerThread.Start(Argument); //Maybe cpu do not start thread Thread.Sleep(20); //Wait..... while (_mWorkerThread.IsAlive) { Thread.Sleep(50); } e.Result = Argument.Result; }

异步完成/停止
当线程停止抛出异常(catch但是不处理)/线程完成时会进入异步完成事件

完成后,将自定义进度条控件实例关闭,释放
将全局的BackgroundWorker实例_mWorker相关事件取消注册,并且检查线程情况
感觉线程情况,如果线程状态为ThreadState.Aborted意味着线程被停止了,调用停止事件,否则调用完成事件

 /// <summary> /// Completed /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Worker_RunWorkCompleted(object sender, RunWorkerCompletedEventArgs e) { try { if (_mfrmProgressForm != null) { _mfrmProgressForm.Close(); _mfrmProgressForm.Dispose(); _mfrmProgressForm = null; } if (_mWorker != null) { _mWorker.DoWork -= Worker_DoWork; _mWorker.RunWorkerCompleted -= Worker_RunWorkCompleted; try { if (_mWorkerThread != null && _mWorkerThread.IsAlive) _mWorkerThread.Abort(); } catch (Exception) { } } //In timer, When stop progress will make thread throw AbortException if (_mWorkerThread != null && _mWorkerThread.ThreadState == ThreadState.Aborted) { WorkStoped?.Invoke(new DoWorkEventArgs<T>(_mWorkArg)); } else { RunWorkCompleted?.Invoke(new RunWorkerCompletedEventArgs<T>(e.Result, e.Error, e.Cancelled)); } } catch (Exception ex) { throw ex; } }

线程开始
检查消息提醒内容 , {0}{1}同于显示异步耗时和".."的个数
在定时器执行方法中,检查_mfrmProgressForm.IsStop是否为true,这个属性标志是否被停止;true则抛出异常
_mfrmProgressForm不为Null则不断修改当前的内容提醒,友好化,实际可以按需处理

 /// <summary> /// Timer Start /// </summary> private void StartTimer() { //Check user ProgressTip if (!ProgressTip.Contains("{0}")) { ProgressTip += "...Elapsed Time{0}{1}"; } if (_mTimer != null) return; //On one sec _mTimer = new Timer(1000); _mTimer.Elapsed += (s, e) => { //progress and it's stop flag (picture stop)|| this stop flag if (_mfrmProgressForm != null && _mfrmProgressForm.IsStop) { if (_mWorker != null) { try { if (_mWorkerThread != null && _mWorkerThread.IsAlive) { if (_mTimer != null && _mTimer.Enabled) { _mTimer.Stop(); _mTimer = null; } _mWorkerThread.Abort(); } } catch (Exception) { } } } if (_mfrmProgressForm != null) { //Callback _mfrmProgressForm.Invoke(new Action<DateTime>(elapsedtime => { DateTime sTime = elapsedtime; //worked time _miWorkerStartDateSecond++; if (_mfrmProgressForm != null) { _mfrmProgressForm.SetProgressValue(_miWorkerStartDateSecond); } //.....count _miShowProgressCount++; if (_miShowProgressCount > 6) { _miShowProgressCount = 1; } string[] strs = new string[_miShowProgressCount]; string ProgressStr = string.Join(".", strs); string ProgressText = string.Format(ProgressTip, _miWorkerStartDateSecond, ProgressStr); if (_mfrmProgressForm != null) { _mfrmProgressForm.TipMessage = ProgressText; } }), e.SignalTime); } }; if (!_mTimer.Enabled) { _mTimer.Start(); } }

最后一步:异步开始 与BackgroundWorker用法一致,只是在最后开始了定时器和进度条控件而已
///

 /// Start AsyncWorl /// </summary> /// <param name="Para"></param> public void AsyncStart(T Para) { //if workeven is null ,express user do not regist event if (DoWork == null) { return; } _miWorkerStartDateSecond = 0; _miShowProgressCount = 0; //init if (_mWorker != null && _mWorker.IsBusy) { _mWorker.CancelAsync(); _mWorker = null; } _mWorker = new BackgroundWorker(); //create progressbar _mfrmProgressForm = new ProgressbarEx(); //add event _mWorker.DoWork += Worker_DoWork; _mWorker.RunWorkerCompleted += Worker_RunWorkCompleted; _mWorker.WorkerReportsProgress = true; _mWorker.WorkerSupportsCancellation = true; //Set Whole Para _mWorkArg = Para; _mWorker.RunWorkerAsync(Para); //Start timer StartTimer(); _mfrmProgressForm.StartPosition = FormStartPosition.CenterParent; _mfrmProgressForm.ShowDialog(); }

到这里,整个的进度条控件已经完成了!

调用
定义一个参数类

/// <summary> /// Para Class /// </summary> public class ParaArg { public DataTable Data { get; set; } public string Msg { get; set; } public Exception Ex { get; set; } }

定义全局的帮助类BackgroundWorkerEx workHelper = null;
调用

 if (workHelper != null || (workHelper != null && workHelper.IsBusy)) { workHelper.Dispose(); workHelper = null; } if (workHelper == null) { workHelper = new BackgroundWorkerEx<ParaArg>(); } workHelper.DoWork += (eve) => { ParaArg args = eve.Argument; try { //ToDo like Thread.Sleep(20000); Thread.Sleep(10000); args.Msg = "...this is bussiness code result"; throw new Exception(""); } catch (Exception ex) { args.Ex = ex; } finally { eve.Result = args; } }; workHelper.RunWorkCompleted += (eve) => { if (eve.Error != null) { //get .net backgroundworker exception; //handle this exception; //return ? } //get your para result ParaArg x = eve.Result; if (x.Ex != null) { //get your bussiness exception; //handle this exception; //return ? } //finially get your need; //MayBe to do some UI hanlde and bussiness logical string sReusltMsg = x.Msg; }; workHelper.WorkStoped += (eve) => { //if stoped ! it means no error; //just get what you want; ParaArg x = eve.Result as ParaArg; btnBegin.Enabled = true; }; //参数 ParaArg arg = new ParaArg() { Msg = "Msg" }; workHelper.AsyncStart(arg); 

最后
其实不管是封装的过程,还是调用,可以说完全就是BackgroundWorker的方式,所以很多BackgroundWorker相关的地方我都没有很详细的去说明;只要看看这个异步模型,就能够很好理解!大家有空也可以实现以下,有问题也可以详细,我比较喜欢交流技术~~~

还有一点就是这个解决方案已经放上Github上了,欢迎大家拉下来用

GitHub地址 欢迎star/fork
原文地址https://www.cnblogs.com/Ligy97/p/12993155.html

原文链接:https://yq.aliyun.com/articles/763059
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章