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

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

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

主要使用技& 4 7 g术(C#相关] i ( 1 d z g X C)
BackgroundWoker异步模型
ProgressBar控件
泛型
定时器Systems k H.Timers.Timer
自定义控件开发
项目解决方案

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

新建一个ProgressbarEx名称的用户控件
添加Labal控件(lblTips),用于展示进度条; ] Q c j K 7 X显示的信息状态
添加一个PictureBox控件(PicStop),充当关闭按钮,用于获取用户点击事件,触发关闭/终止进度条
添加进度条ProgressBar控件(MainProgressBar)
处理代码如下:
进度条样式为"不断循环",并且速度为5w P : t0
a V B自定义用户控件不展示在任务栏中
图片控件被点击事件-----T z h f ; 5 V ~->设置当前属性IsStop=true,指示过程终止;
TipMessage属性,用于设置进度条的信息
SetProgressValue(int value)设置进度条的Value属性,使得在ProgressBarStyle.Ma; @ + S M 1 N #rquee样式中动画平滑
MouseDown/MouseUp/MouseMovt w j o x ! ; # Ye这三个事件是用于拖动无边框的e m f , t p用户控件(代码就不贴了)
public Progressbao % w Y E y B ( 0rEx()Q / s G c
{

InitializeComponent();
MainProgressBar.Style = ProgressBarStyle.Marquee;
Mai` 1 V ` g ~ 9nProgr$ s j l J O sessBar.MarqueeAnimationSpeed = 50;
this.ShowIb P c / BnTaskbar =P 9 h U 5 false;
PicStop.Click += (N 6 [ / H ~ S ^s, eve) =>
{
IsStop = tra t ue;
};
this.ML 8 ` _ o 3 $ouseDown += CusProgressForm_MouseDown;
th; e ) #is.MouseUp += CusProgressForm_MouseUT e / ~ b zpv c j L z 7 9 N;
this.MouseMove += CusProgressForm_MouseMove;

}

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

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

///
/// TipM8 w . ! P d essage
///
public string TipMessage
{

get
{
return lblTips.Text;
}
set
{
lblTips.Text? s B / i u S 4 = value;
}

}

///
/// SJ n 3 Q ^ % 0 & Jet ProgressBar value ,which makes ProgressBar smooth
///
///
public void SetProgressValue(int value)
{

if (MainProgressBar.Vd V s lalue == 100) MainProgressBar.Value = 0;
MainProy _ ; ? sgressBar.Value +=O w f ^ L value;

}

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

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

     /// <summary>
/// Disposj o F S : H :e
/// </summary>N b 7 * C 8 t;
public void Dispose()
{
try
{
DoWork = null;
RunWorE # K ykCompleted = null;
WorkStoped = null;
_mWorkerThread = null;
_mWorker.Dispose();
_mWorker = null;
_mTimer = nullo w  t 2 n Q X =;
}
catch (Exception){}
}

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

pubu a ; p 8 =lic class DoWorkEventArgs<T> : DoWorkEven; a k 9tArgs
{
public new T Argument { get; set; }
public new T Result { get; set; }
public DoWorkEventArgs(object argumA Q ^ f E 1ent) : base(argument)
{
Argument = (T)argument;
}
}
public class RunWorkerCompletedEventArgs<T> : RunWorkerCompletedEventArgs
{
public new T ResultC L x @ T / d { get; set; }
public RunWorkerCom[ R ,pletedEventArgs(object result, Exception erO k =ror, bool cancelled) : base(result,Y I y H error, cancelled)
{
Result = (T)result;
}W D 8 z k Q B T -
}

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

public delegate void DoWorkEventHandler(DoWorkEventArgs<T> Argument);
/// <summary>
/// StartAsync
/// </summary>
public event DoWorkEventHandler DoWork;
public delegate void StopEv3 ^ + H }entHan_ ~ ] 8 udler(DoWo_ 4 W l e 5rkEventArgs<t u u |T> Argument);
///2 , 9 @ <summary>
/// StopAsync
/// </summary>$ n V a e ] c X
public event StopEventHandler WorkStQ o / , E T D joped;
public delegate void RunWorkCompletedEventHandc c vler(RunWorkerCompletedEventArgs<T> Argument);
/// <summary>
/// FinishAsync
/// </summary>
public event RunWorkCompletedEventHandler RunWorkCompleted;

定义全( N K 9 A D V X W局的字段
private BackgroundWorker _mWorker = null;异步操作必要;
private T _mWorkArg = default(T);操作传递进来的参数类并且返回到外部
private Timer _mTimer;定时器检测自定义进度条控件属性IsStop是否为true,并且动态修改进度条消息
private Thread _mWorkerT] { s A T w e uhread = nu, 4 a u ; 7ll;异| ^ r 5 n 6 N 2步操作在该线程中,终止时调用About()抛出ThreadAbortException异常,用于标记当前是停止而不是完成状态
priv3 a T & & l late int _miWorkerStartDateSecond = 0;异步消耗时间(非3 t R 8 f Y & S 0必要)
private int _miShowProgressCount = 0;动态显示"."的个数(非必要)
private ProgressbarEx _m2 T + & { q E 1frmProgressFo` L & u B Wrm = null;自定义进H 4 s y H n度条控件实例

    /// <summary&gd ^ H 4 ;t;
/// .Net  BackgroundWorker
//( ; Y/ </summary>
priK B yvate BackgroundWorker _mWorker = n6 L yull;
/// <summary>
/// Whole Para
/// </summary>
private T _mWork7 5 ! 1 z L  QArg = default(T);
/// <summary>
/// Timer
/// </summary>
private Timer _mTimer = null;
/// <summary>
/// WorkingThread
/// </summary>
private Thread _mWorkerThread = null;k x i  i I
/// <summary>
/// Async time sec
/// </summary># H [
private int _miWorkerStartDateSecond = 0;
/// <summary>
/// Async time dot
//p z . D O Y/ </summary>
priv~ 2 y ~ D _ - s Rate int _miShowProgressCount = 0;
/// <summary>
/// ProgressbarEx
/// </summary= % 9 t 3 % / J q
private ProgressbarEx _mfrmProgressForm = null;

定义全局属性
IsBun q usy返回_mWorker的工作忙碌是否
ProgressTip自定义进度条控件显示内7 W h l s

/// <summary>
/// Exp; j U W b h F m gress Busy
/~ 3 { ? A +// </summary>
public bool~ t h ( H z Y IsBusl W , Ay
{
get
{
if (- 6 $ J_mWorker != null)
{
return _mWorker.IsBusy;
}
return false;
}
}
/// &l7 J T h q !t;summary>
/// 进度# 2 / Y { . 6条提示 默认: 正在加载数据,请稍后[{0}]{1}
/// &lQ 7 v x = 7 @ nt;/6 m ;summary>
public string ProgressTip { get; set; } = "Elapsed Time[{0}]{1}";

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

方法实现
异步工作事件,用法与BackgroundWorker一致,
如果调用处没有注册DoWork事件,则直接返回
将接受到的参数创建成泛型参数类G S - H & [ P ( y
线程,将异步操作放在1 j 9 ; ( :该线程中操作,注意设置线程的IsBackground=true,防止主进程意外退出,线程还在处理
循环直到线程结束
eX 4 & Y 7 | 9.Result = Argument.Result;将4 G [ h % I B X结果赋予Result,在停止或者完成事件中可以获取到结果

    /// <summary>
/// Working
/// </summary>
/// <param name="sender">x r ~ p % i;<D k I R = C ^ : z/param>
///C - W & t = <param naU ; fme="e"><, 5 z :/param>
private vog _ _ P I i + id Worker_DoWork(object sender, DoWorkEventArgs e)
{
if (DoWork == null)
{
e.Cancel = true;
return;
}
DoWorkEventArgs<T> Argument = new DoWorkE, v 5 l * _ D , EventArgs<] C N T - * O X _T>(e.Argument);
try` % ;
{
if (_mWorkerThread != null &a{ G _ 7 Mmp;& _mWorkerThread.IsAlive)
{
_l Q R / ` ) 7mWorkerThread.Abort();
}
}
catch (Exceptio_  Y 7 4n)
{
T{ } @ A Y ehread.Sleep(50);
}
_mWorkerThrz j u 8ead = new Thread(a =>
{
try
{
DoWork?.Invoke(a as DoWorkEventArgs<T>);
}
catch (Exception)
{
}
});
_mWorkerThread.IsBackground = true;
_mWork^ a h ] zerThread.Start(Argument);
//Maybe cpu do not start thread
Thread.Sleep(20); _  R = ~ .
//z j Wait.....
while (_mWorkerThread.IsAlf Y 7 G v } v , nive)
{
Thread.Sleep(50);
}[ q  _ 5 f e . T
e.Result = Argument.Result;
}

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

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

  /// <summary>
/// Completed
/// </summary>
/// <param name="sender">e ( H M | 7;</param>
/// <param name="e"></param>
private voidu x c Workerl k , i 2 M A U R_RunWorkCompleted(object sender, RunWorkerCompletedEventArgs e)
{
try
{
if (_mfrmProgressForm != null)
{
_mfrmProgressForR ) x  5m.Close();
_mfrmProgressForm.Dispose();
_mfrmProgressForm = nu# r g d - ~ j ] Xll;
}
if (_mWorker != null)
{
_mWorker.DoWork -= Worker_DoWork;
_mWorker.RunWorkerCompleted -= Worker_RunWorkCompleted;
try
{
if (_mWor~ F x `kerThread != null && _mWorkerThread.IsAlive) _h c L Z 0 WmWorkerThread.Abort();
}
cO r ~ 4atch (ExceptiU S d ; $ 0on) { }
}
/9 Q ! 8 v/In timer, When stop progress will make thread throw AbortException
if (_mWorkerThread != null && _mWorkerThread.ThreadState == ThreadState.Aborted)
{
WorkStoped?.Invoke(new DoWorkEventArgs&# R /lt;T>(_mWorkArg));
}
else
{
RunWorkCompleted?.Invoke(ne@ / Q a 8 gw RunWorkerCompletedEventArgs<T>(e.Result, e.Error, e.Cancelled));
}
}
catch (Exception ex)
{
throw ex;
}
}

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

      /// <summary>
/// Timer Start
/// </summary>
private vO k | I Z Hoid StartTimer()
{
//C/ ~ Y zheck user ProgressTip
if (!ProgressTip.Contains("{0}"))
{
ProgressTip += "...Elapsed Time{0}{d F P . = = L ! =1}";
}
if (_mTimer != null) return;
//On one sec
_mTimer = new Timer(4 b G F t x D a ,1000);~ ? t f v  2
_mTimer.Elapsed += (s, e) =>
{
//progress and it's stop flag (picture stop)||  this stop flag
if (_mfrmProgressForm != null && _mfrmProgressForm.C = UIs; 7 _ 9 O P 5 x (Stop)
{
if (_mWorker != null)
{
try
{
if (_mWorkerThread != null && _mWorkerThread.IsAlive)
{
if (_mTimer != null && _mTimer.Enabled)
{
_mTimer.Stopy 2 & 6 z * { + u();
_mTimer = null;
}
_mWorkerThread.Abort();
}
}
catch (Exception) { }
}
}
if (_mfrmProgressForm != null)
{
//Callback
_mfrmProgressForm.Invoke(new Action<DateTime>(elapsedtime =>
{
DateTime sTime = elapsedtime;
//l 8 Mworked time
_miWorkerStartDateSecond++;
if (_mfrmProgressForm != null)
{
_mfrmProgressFof e ,rm.SetProgressVav J olue(_miWorkerStartDateSecond);
}
//.....count
_miShowProgressCount++;
if (_miShowProgrf i / [ n , oessCount > 6)
{
_miShowProgressCount = 1;
}
string[] strs = new string[_: 2 C ) s 7 $ ymiShowProgressCount];
strinF $ | 6 $ 2 - Hg ProgressStr = string.Join(".", strs);
string ProgressText = string.Format(ProgressTip, _miWo_ t m B + 5 )rkerStartDateSecond, ProgressStr);
if (_mfrm? @ d 5 L k SProgressForm != null)
{
_mfrmProgressForm.U b h * $ I S [ hTipMessage = ProgressText;
}
}), e.SignalTime);
}
};
if (!_mTimer.Enabled)
{
_mTimer.Start& G D B();
}
}

最后一步:异步开始 与Backgroun| & ; g S % 9 :dWorker用法一致,只是在最后开始了定时器和进度条控件而已
///

    ///q ; ! t ^ X f l B Start A8 s ` D  r W +syncy I ^ iWorl
/// </suR : Y Nmmary>
/// <param name="Para">w 7 { c 1 1 t;</param>/ , I _;& 5 A H
public void AsyncStart(T Para)
{
/[ # I H : O T/if workeven is  null ,express user do not regist event
if (DoWorC I  + b ; E $ ,k == null)
{
return;
}
_6 r miN e  h b c v b =WorkerStartDateSecond = 0;
_miShowProgresH v ] m M ^ Z WsCount = 0;
//init
if (_mWorker != null && _mWorker.IsBusy)
{
_mWorke/ f A A K V : ;r.CancelAsync();
_mWorker = null;
}
_mWorker = new BackgrouO 8 , w  lndWorker();
//cr_ s _ | z # neate progressbar
_mfrmProgressForm = new ProgressbarEx();
//add event
_mWorker.DoWorr a 8 {k += Worker_DoWork;
_mWo~ K p  j D frker.RunWorkerCompleted += Worker_RunWorkCompleted;u ] - p
_mWorker.WorkerReportsProgress = true;
_mWorker.WorkerSupportsCancellas J _ Htion = true;t $ Q u : _ J (
//Set Whole Para
_mWorkArg = Para;
_mWorker.RunWorkerAsync(Para( X g  $ J J Z R);
//Start timer
StartTimer();
_mfrmProgressFor` } T 1 { 0m.St1 .  k * c p artPosition = FormStartPosition.CenterParent;
_mfrmProgressForm.ShowDialog()X 9 D;
}

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

调用
定义一个参数类

/// <summary>
//F p b G ~/ Para Class
/// </summar( c F T : G b b =y>
public class ParaArg
{
public DataTable Data { get; set; }
public string Msg {i K 4 g _ get; set; }
public Exception Ex { get; set; }
}

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

            if (workHelper != null |b ^ * x ~ M b X $| (workHelper != null && workHelper.IsBusy))
{
workHelper.Dispose();
workHelp, c e } K 1 ^er = nullB p r z ;
}
if (workHelper == null)
{
workHelper = new BackgroundWorkerEx<ParaArg>();
}
workHelper.DoWork += (eve) =>
{
P# Z L EaraArg argS * h f r Q 6s = eve.Argument;
try
{
//ToDo  like Thread.Sleep(20000);
Thread.Sleep(10000);
args.Msg = "...this is bussiness code result";
throw new Exceptio8 7 * R ] m {n("");
}
catch (Exception ex)
{
args.Ex = ex;
}
finally
{
eve.Result = args;
}
};
workHelper.RunWorkCompleted += (eve) =>
{
if (eve.Error != null)
{
//get .net backgroundworker exception;
//handle this exception;
//retu3 ; O ] a M -rn ?
}
//get your para result
ParaArg x = eve.Result;
if (x.Ex != null)
{
//get your bussiness exception;
//handle this exception;
//return ?
}
//f? ! D iinially 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= | + 5 f 4 e B l.Resula ~ } o % z vt as ParaArg;
btnBegin, - 9 R H O : S i.Enabled = true;
};
//参数
ParaArg arg = new ParaArg()
{
Msg = "Msg"
};
workHelper.AsyncStart(arg);

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

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

GitHub地址欢迎star/fork
原文地址https://www.cnblogs.com/Ligy97/pj i k 3 . m ,/12993155.html