---------------201504170911更新---------------
更新内容:删除bgwUI新增的Start方法,改为通过new修饰符+可选参数的方式同时覆盖基类(BackgroundWorker)的RunWorkerAsync有参和无参俩方法。所以执行任务仍旧使用熟悉的RunWorkerAsync即可,忘掉蹩脚的Start。在此要感谢园友【新的开始】在评论中的指点,非常感谢!
---------------20150416原文(已更新)---------------
适用环境:.net 2.0+的Winform项目
这是上一篇【分享带等待窗体的任务执行器一枚】的姊妹篇,建议先看看那篇文章了解一下相关背景。这里简单介绍一下,两个方案的共同目的都是在执行耗时任务时向用户显示一个模式窗体(我称等待窗体),通过该窗体,任务可以向用户报告执行进度,用户也可以通过它干预任务的执行(也就是取消~如果任务允许被终止的话),等于就是在任务与用户之间通过一个等待窗体来进行信息传递。这样的需求应该是很常见的,注重用户体验的开发者都不可能让用户眼巴巴的面对一个卡死掉的界面,所以相信在类似场景中,大家都有各自的处理手段,例如异步执行任务,同时在业务窗体上弄个滚动条什么的,比如这样:
这样的手段有的猿友可能已经形成了很完善的通用方案,比我这个好上百倍都不止(在此也恳请路过老鸟不吝分享自己的或自己知道的现成好方案),有的猿友则可能还是具体情况具体处理,没有一个通用方案,而我在做的,就是把我的方案分享出来,让还没有类似轮子的猿友拿去后,经过简单处理就能实现效果,同时,也希望得到老鸟的指点,不断完善。
上一篇分享的是一个叫做WaitUI的执行器,可以执行任何方法,使用简单。而这一篇分享的是一个叫做BackgroundWorkerUI的东东(下文简称bgwUI),看名字就知道它是基于BackgroundWorker(下文可能简称bgw)组件实现的,所以如果你更习惯bgw的使用方式,这个适合你。先看一下使用效果:
private void button2_Click(object sender, EventArgs e)
{
//构造函数的另一个重载可传入自定义等待窗体的实例
using (BackgroundWorkerUI bgwUI = new BackgroundWorkerUI(/*new MyWaitForm()*/))
{
bgwUI.WorkerSupportsCancellation = true;//允许取消任务
bgwUI.DoWork += bgwUI_DoWork;
//bgwUI.ProgressChanged += bgwUI_ProgressChanged;//虽然不需要,但仍可注册ProgressChanged事件做其它事
bgwUI.RunWorkerCompleted += bgwUI_RunWorkerCompleted;//亦可注册RunWorkerCompleted事件
bgwUI.RunWorkerAsync();
}
}
void bgwUI_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorkerUI bgwUI = sender as BackgroundWorkerUI;
//可以通过bgwUI的一组公开属性和方法更新等待窗体
//bgwUI.CancelControlVisible = true;//设置取消任务的控件的可见性,默认该属性会根据WorkerSupportsCancellation设置,但仍可以自由设置
bgwUI.BarStyle = ProgressBarStyle.Continuous;//设置滚动条样式(默认是Marquee:循环梭动式)
bgwUI.BarMaximum = 100; //设置滚动条值上限(默认是100)
bgwUI.BarMinimum = 0; //设置滚动条值下限(默认是0)
bgwUI.BarStep = 1; //设置滚动条步进幅度(默认是10)
bgwUI.BarVisible = true; //设置滚动条是否可见(默认是true:可见)
int i;
for (i = Convert.ToInt32(e.Argument); i <= 100; i++)
{
if (bgwUI.CancellationPending)//老样子,访问CancellationPending获知用户是否取消任务
{
e.Cancel = true;
return;
}
//更新等待窗体不需要调用ReportProgress(),也不需要WorkerReportsProgress支持
bgwUI.WorkMessage = i.ToString();//设置任务进度描述
bgwUI.BarValue = i; //设置任务进度值
//CancelControlVisible可以反复设置,不受WorkerSupportsCancellation限制
//if (i % 10 == 0) { bgw.CancelControlVisible = false; }
//else if (i % 5 == 0) { bgw.CancelControlVisible = true; }
Thread.Sleep(50);
}
e.Result = i;
}
void bgwUI_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (e.Cancelled)
{
MessageBox.Show("任务已取消!");
}
else if (e.Error != null)
{
MessageBox.Show("任务有异常!" + e.Error.Message);
}
else
{
MessageBox.Show("任务完成。" + e.Result);
}
}
这里只讲区别,没讲到的表示与bgw一致,不熟悉bgw用法的猿友请MSDN。先看类图:
从类图可看出bgwUI是继承于bgw的子类。
BackgroundWorkerUI.cs仅包含class BackgroundWorkerUI,它用到的WaitForm.cs请到上一篇文章取用,帮园子节约点空间~哈。
using System;
using System.ComponentModel;
using System.Windows.Forms;
namespace AhDung.WinForm
{
/// <summary>
/// 带等待窗体的BackgroundWorker。报告进度用一组UI操作方法
/// </summary>
public class BackgroundWorkerUI : BackgroundWorker
{
readonly IWaitForm waitForm;//等待窗体
Form activeForm;//等待窗体显示前的活动窗体
bool formClosed;//指示等待窗体是否已被关闭
#region 一组操作等候窗体UI的属性/方法
/// <summary>
/// 获取或设置进度描述
/// </summary>
public string WorkMessage
{
get
{
if (waitForm.InvokeRequired)
{
return waitForm.Invoke(new Func<string>(() => waitForm.WorkMessage)) as string;
}
return waitForm.WorkMessage;
}
set
{
if (waitForm.InvokeRequired)
{
waitForm.BeginInvoke(new Action(() => waitForm.WorkMessage = value));
return;
}
waitForm.WorkMessage = value;
}
}
/// <summary>
/// 获取或设置进度条可见性
/// </summary>
public bool BarVisible
{
get
{
if (waitForm.InvokeRequired)
{
return Convert.ToBoolean(waitForm.Invoke(new Func<bool>(() => waitForm.BarVisible)));
}
return waitForm.BarVisible;
}
set
{
if (waitForm.InvokeRequired)
{
waitForm.BeginInvoke(new Action(() => waitForm.BarVisible = value));
return;
}
waitForm.BarVisible = value;
}
}
/// <summary>
/// 获取或设置进度条动画样式
/// </summary>
public ProgressBarStyle BarStyle
{
get
{
if (waitForm.InvokeRequired)
{
return (ProgressBarStyle)(waitForm.Invoke(new Func<ProgressBarStyle>(() => waitForm.BarStyle)));
}
return waitForm.BarStyle;
}
set
{
if (waitForm.InvokeRequired)
{
waitForm.BeginInvoke(new Action(() => waitForm.BarStyle = value));
return;
}
waitForm.BarStyle = value;
}
}
/// <summary>
/// 获取或设置进度值
/// </summary>
public int BarValue
{
get
{
if (waitForm.InvokeRequired)
{
return Convert.ToInt32(waitForm.Invoke(new Func<int>(() => waitForm.BarValue)));
}
return waitForm.BarValue;
}
set
{
if (waitForm.InvokeRequired)
{
waitForm.BeginInvoke(new Action(() => waitForm.BarValue = value));
return;
}
waitForm.BarValue = value;
}
}
/// <summary>
/// 获取或设置进度条步进值
/// </summary>
public int BarStep
{
get
{
if (waitForm.InvokeRequired)
{
return Convert.ToInt32(waitForm.Invoke(new Func<int>(() => waitForm.BarStep)));
}
return waitForm.BarStep;
}
set
{
if (waitForm.InvokeRequired)
{
waitForm.BeginInvoke(new Action(() => waitForm.BarStep = value));
return;
}
waitForm.BarStep = value;
}
}
/// <summary>
/// 使进度条步进
/// </summary>
public void BarPerformStep()
{
if (waitForm.InvokeRequired)
{
waitForm.BeginInvoke(new Action(() => waitForm.BarPerformStep()));
return;
}
waitForm.BarPerformStep();
}
/// <summary>
/// 获取或设置进度条上限值
/// </summary>
public int BarMaximum
{
get
{
if (waitForm.InvokeRequired)
{
return Convert.ToInt32(waitForm.Invoke(new Func<int>(() => waitForm.BarMaximum)));
}
return waitForm.BarMaximum;
}
set
{
if (waitForm.InvokeRequired)
{
waitForm.BeginInvoke(new Action(() => waitForm.BarMaximum = value));
return;
}
waitForm.BarMaximum = value;
}
}
/// <summary>
/// 获取或设置进度条下限值
/// </summary>
public int BarMinimum
{
get
{
if (waitForm.InvokeRequired)
{
return Convert.ToInt32(waitForm.Invoke(new Func<int>(() => waitForm.BarMinimum)));
}
return waitForm.BarMinimum;
}
set
{
if (waitForm.InvokeRequired)
{
waitForm.BeginInvoke(new Action(() => waitForm.BarMinimum = value));
return;
}
waitForm.BarMinimum = value;
}
}
/// <summary>
/// 获取或设置取消任务的控件的可见性
/// </summary>
public bool CancelControlVisible
{
get
{
if (waitForm.InvokeRequired)
{
return Convert.ToBoolean(waitForm.Invoke(new Func<bool>(() => waitForm.CancelControlVisible)));
}
return waitForm.CancelControlVisible;
}
set
{
if (waitForm.InvokeRequired)
{
waitForm.BeginInvoke(new Action(() => waitForm.CancelControlVisible = value));
return;
}
waitForm.CancelControlVisible = value;
}
}
#endregion
/// <summary>
/// 初始化组件
/// </summary>
public BackgroundWorkerUI()
: this(new WaitForm())
{ }
/// <summary>
/// 初始化组件并指定等待窗体
/// </summary>
/// <param name="fmWait">等待窗体</param>
public BackgroundWorkerUI(IWaitForm fmWait)
{
if (fmWait == null) { throw new WaitFormNullException(); }
waitForm = fmWait;
waitForm.UserCancelling += WaitForm_UserCancelling;//注册用户取消任务事件
}
/// <summary>
/// 开始执行后台操作
/// </summary>
/// <param name="argument">要在DoWork事件处理程序中使用的参数</param>
/// <remarks>通过可选参数可以同时覆盖基类无参RunWorkerAsync,一石二鸟</remarks>
public new void RunWorkerAsync(object argument = null)
{
Form f;
activeForm = (f = Form.ActiveForm) != null && f.IsMdiContainer ? f.ActiveMdiChild : f;//记录当时的活动窗体
waitForm.CancelControlVisible = this.WorkerSupportsCancellation;
formClosed = false;
base.RunWorkerAsync(argument);
//这里要判断一下,极端情况下有可能还没等ShowDialog,窗体就已经被关闭了
if (!formClosed) { waitForm.ShowDialog(); }
}
/// <summary>
/// 用户请求取消任务时
/// </summary>
private void WaitForm_UserCancelling(object sender, EventArgs e)
{
this.CancelAsync();
}
protected override void OnRunWorkerCompleted(RunWorkerCompletedEventArgs e)
{
waitForm.Hide();
formClosed = true;
//上面Hide后,原活动窗体会在该方法完成后才会重新获得焦点,所以必须加以干预让原窗体现在就获得焦点
//否则随后的RunWorkerCompleted事件中弹出的模式窗体会有不正常的表现
if (activeForm != null && !activeForm.IsDisposed) { activeForm.Activate(); }
base.OnRunWorkerCompleted(e);
}
//资源释放
protected override void Dispose(bool disposing)
{
IDisposable form;
if (disposing && (form = waitForm as IDisposable) != null) { form.Dispose(); }
base.Dispose(disposing);
}
}
}
-----------------分隔线-----------------
下面的内容对于方案使用来说不是必须的,赶时间你可以先闪。
-文毕-