---------------201504170911更新---------------

更新内容:删除bgwUI新增的Start方法,改为通过new修饰符+可选参数的方式同时覆盖基类(BackgroundWorker)的RunWorkerAsync有参和无参俩方法。所以执行任务仍旧使用熟悉的RunWorkerAsync即可,忘掉蹩脚的Start。在此要感谢园友【新的开始】在评论中的指点,非常感谢!

---------------20150416原文(已更新)---------------

适用环境:.net 2.0+的Winform项目

这是上一篇【分享带等待窗体的任务执行器一枚】的姊妹篇,建议先看看那篇文章了解一下相关背景。这里简单介绍一下,两个方案的共同目的都是在执行耗时任务时向用户显示一个模式窗体(我称等待窗体),通过该窗体,任务可以向用户报告执行进度,用户也可以通过它干预任务的执行(也就是取消~如果任务允许被终止的话),等于就是在任务与用户之间通过一个等待窗体来进行信息传递。这样的需求应该是很常见的,注重用户体验的开发者都不可能让用户眼巴巴的面对一个卡死掉的界面,所以相信在类似场景中,大家都有各自的处理手段,例如异步执行任务,同时在业务窗体上弄个滚动条什么的,比如这样:

这样的手段有的猿友可能已经形成了很完善的通用方案,比我这个好上百倍都不止(在此也恳请路过老鸟不吝分享自己的或自己知道的现成好方案),有的猿友则可能还是具体情况具体处理,没有一个通用方案,而我在做的,就是把我的方案分享出来,让还没有类似轮子的猿友拿去后,经过简单处理就能实现效果,同时,也希望得到老鸟的指点,不断完善。

上一篇分享的是一个叫做WaitUI的执行器,可以执行任何方法,使用简单。而这一篇分享的是一个叫做BackgroundWorkerUI的东东(下文简称bgwUI),看名字就知道它是基于BackgroundWorker(下文可能简称bgw)组件实现的,所以如果你更习惯bgw的使用方式,这个适合你。先看一下使用效果:

功能:

  • 在bgwUI执行任务期间(DoWork事件)显示一个等待窗体,任务执行完成后自动消失。任务执行完是指DoWork事件跑完,而不是RunWorkerCompleted事件完,也就是RunWorkerCompleted执行期间已经没有等待窗体了
  • 等待窗体可以自定义,但须实现IWaitForm接口
  • 在DoWork事件中可以访问一组bgwUI提供的属性和方法更新等待窗体上的文本和进度,以及可以控制等待窗体上的【取消】按钮是否可见。是的,更新控件不需要再用ProgressChanged事件,事实上等待窗体实例(一个IWaitForm实例)对调用者是隐藏的,你不能也不需要直接对它操作,一切通过bgwUI进行
  • 如果任务允许被终止,即bgw.WorkerSupportsCancellation为true,等待窗体会显示【取消】按钮,用户可以通过点击它发出终止任务的请求,你可以像老样子一样,在DoWork中访问CancellationPending获知该请求
  • 其余功能与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)
bgwUI.BarMinimum = ; //设置滚动条值下限(默认是0)
bgwUI.BarStep = ; //设置滚动条步进幅度(默认是10)
bgwUI.BarVisible = true; //设置滚动条是否可见(默认是true:可见) int i;
for (i = Convert.ToInt32(e.Argument); i <= ; 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();
}
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);
}
}

使用示例

与BackgroundWorker的用法区别:

这里只讲区别,没讲到的表示与bgw一致,不熟悉bgw用法的猿友请MSDN。先看类图:

从类图可看出bgwUI是继承于bgw的子类。

  • bgwUI重载了一个可传入IWaitForm实例的构造函数,就是可以传入自定义等待窗体,使用无参构造函数的话,就使用默认的等待窗体,即WaitForm
  • DoWork事件中可以直接使用bgwUI的一组属性和方法(WorkMessage、BarValue、BarPerformStep等)更新等待窗体,不再需要注册ProgressChanged事件,完了在DoWork中bgw.ReportProgress,并且连WorkerReportsProgress属性都不需要置为true。但是虽然更新等待窗体不需要ProgressChanged事件,但如果你仍然需要该事件做一些其它事,仍然可以注册并照常使用

方案源码:

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);
}
}
}

BackgroundWorkerUI.cs

-----------------分隔线-----------------

下面的内容对于方案使用来说不是必须的,赶时间你可以先闪。

实现说明:

  • 之所以在构造时就要传入等待窗体,而且不提供WaitForm这样的属性让调用者随时能get/set等待窗体,是为了避免做一些蛋疼的控制,因为这样的话,当设置bgwUI.BarVisible这些属性的时候,等待窗体有可能是null,那显然就要增加null的判断,还有很多其它情况要考虑。就算是现在这样,调用者不小心传入一个已经Close/Dispose的等待窗体也没办法,这个问题WaitUI方案也同样存在,也许后面我会改为仅允许传入等待窗体的Type,完了在方案中全权负责等待窗体的从生到死,避免外部破坏
  • 为什么有个activeForm字段。这个在源码里也有说明,就是要让等待窗体Hide后,base.OnRunWorkerCompleted执行前,让原先那个活动窗体立即获得焦点,activeForm就是用来记录原先那个活动窗体用的。至于为什么要做这个干预,是因为原活动窗体不会在等待窗体Hide后立即获得焦点,而是要等bgwUI.OnRunWorkerCompleted整个方法执行完才会获得,也就是说,base.OnRunWorkerCompleted执行期间是没有活动窗体的,base.OnRunWorkerCompleted执行的就是RunWorkerCompleted事件处理程序,换句话说,RunWorkerCompleted事件执行时没有活动窗体,那么在事件中弹出的模式窗体就不会有正常的表现,至于怎么个不正常,无法言表,自己体会。总之根本问题就是,当某个窗体在非活动状态下弹出模式窗体,那个模式窗体就会不正常,要问如何才能在非活动状态弹出模式窗体,这个可以自己用timer实现。而为什么会不正常,这个我也想知道,还请高人解答
  • 有关IWaitForm和WaitForm的请参看上一篇

-文毕-

【C#】带等待窗体的BackgroundWorker的更多相关文章

  1. 【C#】分享带等待窗体的任务执行器一枚

    -------------201504161039更新------------- 更新内容: IWaitForm接口删除System.Windows.Forms.DialogResult Dialog ...

  2. Devexpress 等待窗体

    加载窗体以及等待窗体 程序加载时,需要等待加载完成后在显示 窗体显示顺序 1. 给用户看的等待窗体 2. 加载完成后的主窗体 代码如下: 1. 等待窗体代码 #region using using S ...

  3. c# winForm 等待窗体的实现

    最近在做一个项目,需要用到等待窗体,在DevExpress下面有SplashScreen控件可以使用,同时也有ProgressIndicator控件能用,但是如果没有用Dev开发的话,我们就需要自定义 ...

  4. C#关于等待窗体(转)

    c#.net 中如果想在主窗口A里点击打开新窗口B(因为要数据库操作,Bload需一小段时间)之前弹出带有滚动条等待子窗口C来提示用户没有死机,应该怎么做?我用多线程打开了c窗口,但是问题:1.C窗口 ...

  5. C#Winform之等待窗体

    窗体主要代码: ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 ...

  6. C#中的“等待窗体”对话框

    这篇文章向您展示了如何在c#.net Windows窗体应用程序中创建一个等待窗体对话框.创建一个新表单,然后输入您的表单名称为frmWaitForm.接下来,将Label,Progress Bar控 ...

  7. (二十三)c#Winform自定义控件-等待窗体

    前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. 开源地址:https://gitee.com/kwwwvagaa/net_winform_custom_control ...

  8. winfrom创建转圈等待窗体

    第一步:创建一个WaitForm public partial class WaitForm : Form { ; private ArrayList images = new ArrayList() ...

  9. DevExpress窗体加载等待

    using DevExpress.XtraEditors; using DevExpress.XtraSplashScreen; using System; using System.Collecti ...

随机推荐

  1. [ACM_模拟] POJ1068 Parencodings (两种括号编码转化 规律 模拟)

    Description Let S = s1 s2...s2n be a well-formed string of parentheses. S can be encoded in two diff ...

  2. 常用CSS优化总结——网络性能与语法性能建议

    在前端面试中最常见的问题就是页面优化和缓存(貌似也是页面优化),被问了几次后心虚的不行,平然平时多少会用到一些,但突然问我,很难把自己知道的都说出来.页面优化明显不是一两句能够说完的,这两天总结了一下 ...

  3. 利用定时器实时显示<input type="range"/>的值

    <!doctype html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  4. 缓存篇(Cache)~第一回 使用static静态成员实现服务器端缓存(导航面包屑)

    返回目录 今天写缓存篇的第一篇文章,在写完目录后,得到了一些朋友的关注,这给我之后的写作带来了无穷的力量,在这里,感谢那几位伙伴,哈哈! 书归正传,今天我带来一个Static静态成员的缓存,其实它也不 ...

  5. Nodejs·内存控制

    之前有考虑过Node中的内存管理,但是没想到Node的内存机制与JVM如此相像. 看完这部分的内容,基本可以了解Node中的内存使用技巧: 1 尽量不要做过多的缓存 2 使用队列应该有限制 3 注意全 ...

  6. Kafka与Logstash的数据采集对接 —— 看图说话,从运行机制到部署

    基于Logstash跑通Kafka还是需要注意很多东西,最重要的就是理解Kafka的原理. Logstash工作原理 由于Kafka采用解耦的设计思想,并非原始的发布订阅,生产者负责产生消息,直接推送 ...

  7. WPF入门教程系列二——Application介绍

    一.Application介绍 WPF和WinForm 很相似, WPF与WinForm一样有一个 Application对象来进行一些全局的行为和操作,并且每个 Domain (应用程序域)中仅且只 ...

  8. MySQL5.7.13源码编译安装指南

    系统 CenterOs 6.5 1.安装依赖包(cmake make gcc等,其实好多都有了,不需要更新,为了防止世界被破坏,就装下) yum install gcc gcc-c++ -yyum i ...

  9. Package gp in the OpenCASCADE

    Package gp in the OpenCASCADE eryar@163.com China 一.简介 Introduction to Package gp gp是几何处理程序包(Geometr ...

  10. Bootstrap3.0学习第十轮(下拉菜单、按钮组、按钮式下拉菜单)

    详情请查看http://aehyok.com/Blog/Detail/16.html 个人网站地址:aehyok.com QQ 技术群号:206058845,验证码为:aehyok 本文文章链接:ht ...