Winform 实现进度条弹窗和任务控制

作者:独立观察员•博客 时间:2023-06-20 04:27:09 

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

一、弹窗前台

首先提供一个 Winform 控件居中的小技巧:

将控件放在 TableLayoutPanel 容器中,然后将控件的 Anchor 属性设置为 None,这样控件就能在容器中居中了:

Winform 实现进度条弹窗和任务控制

将容器的 Anchor 属性设置为 Top, Left, Right,这样容器就能随着窗口左右拉伸了:

Winform 实现进度条弹窗和任务控制

最终弹窗界面如下:

Winform 实现进度条弹窗和任务控制

使用了 CSkin 界面库(v16.1.14.3),(注意:如果拖拽 dll 到工具箱拖不了,可以使用右键复制粘贴的方式),窗体继承 Skin_DevExpress,进度条使用 SkinProgressBar,按钮使用 SkinButton,主要是使用了一些圆角效果:

Winform 实现进度条弹窗和任务控制

二、弹窗后台

先添加两个事件供外界订阅,分别为窗体载入时触发的执行操作事件,和点击中止按钮后触发的终止操作事件:


/// <summary>
/// 执行操作事件
/// </summary>
public event Action OperateAction;

/// <summary>
/// 终止操作事件
/// </summary>
public event Action AbortAction;

/// <summary>
/// 中止按钮点击事件
/// </summary>
private void btn_Abort_Click(object sender, EventArgs e)
{
 AbortAction?.Invoke();
 DialogResult = DialogResult.Abort;
 //Close(); //不需要手动关闭;
}

/// <summary>
/// 窗体载入事件
/// </summary>
private void FormProgressDialog_Load(object sender, EventArgs e)
{
 Task.Factory.StartNew(() =>
 {
   OperateAction?.Invoke();
   DialogResult = DialogResult.OK;
 });
}

点击中止按钮后还将弹窗结果设为 Abort,会自动关闭弹窗;而业务操作正常执行完毕,弹窗结果为 OK。

供外界设置文本信息以及进度条进度的方法如下:


/// <summary>
/// 设置显示信息(值为null时保持不变)
/// </summary>
/// <param name="rtfTitleContent">富文本格式的标题内容</param>
/// <param name="totalMessage">总体消息</param>
/// <param name="currentMessage">当前消息</param>
public void SetInfo(string rtfTitleContent = null, string totalMessage = null, string currentMessage = null)
{
 if (rtfTitleContent != null) rtb_Title.Rtf = rtfTitleContent;
 if (totalMessage != null) lbl_Total.Text = totalMessage;
 if (currentMessage != null) lbl_Current.Text = currentMessage;
}

/// <summary>
/// 设置进度
/// </summary>
/// <param name="currentValue">当前数值</param>
/// <param name="totalValue">总数值</param>
public void SetProsess(double currentValue, double totalValue)
{
 try
 {
   progressBar.Value = (int)(currentValue / totalValue * 100);
 }
 catch (Exception ex)
 {
   Console.WriteLine(ex);
 }
}

剩下就是两个设置富文本框 RichTextBox 的方法,包括设置彩色内容和隐藏 RichTextBox 光标的方法,文末会给出代码地址,此处不再赘述。

三、使用方法

首先映入眼帘的是两个成员变量,一个是用于任务取消的 CancellationTokenSource 对象,另一个是用于线程同步的 AutoResetEvent 对象(用于取消任务后的一些信息同步);然后是主测试方法(一个按钮点击事件方法)中的一些信息设置:

Winform 实现进度条弹窗和任务控制

然后设置 CancellationTokenSource 对象的 Token,给它注册一个取消任务时调用的委托方法,里面先等待同步信号结果再进行本次执行结果的判断:

Winform 实现进度条弹窗和任务控制

接下来订阅弹窗中的那两个事件,在执行操作事件中开启任务,并传递 Token;在中止事件中停止任务:

Winform 实现进度条弹窗和任务控制

需要注意的是,停止任务后,任务内部并不会自己停止,需要判断 Token 的 IsCancellationRequested 字段来决定相应的操作,比如结束循环。然后,因为在之前注册的取消的委托方法中,进行了等待,所以我们在执行完业务方法(BusinessMethod)并设置好相关状态值后,需要判断任务是否取消,如果取消,说明注册的取消的委托方法中已经在等待了,所以要调用 Set () 进行放行。

有人可能就会问了,foreach 循环开始时不是判断过是否取消了吗?这里怎么又判断?这是因为,比如在一轮循环中,已经执行过了开头的是否已取消的判断(IsCancellationRequested 为 false),开始执行耗时的业务方法了,此时用户点击中止按钮,IsCancellationRequested 被置为 true,所以业务方法执行后再次判断会得到最新的状态,然后,循环将在下一轮开始时结束。

另外,由于实际使用这个的项目是 .NET 4.0 框架,所以 Task 的一些方法没有,大家用新框架的话可以使用新方法。或者使用 Microsoft.Bcl.Async 包,然后使用 TaskEx。

继续流程,接下来以模态框方式弹出窗口,并获取结果。业务处理方法中模拟了耗时操作并返回是否成功。

Winform 实现进度条弹窗和任务控制

最后给出完整代码:


#region 测试任务进度条弹窗

private CancellationTokenSource _Cts; //任务取消令牌;
private AutoResetEvent _AutoResetEvent = new AutoResetEvent(false);//参数传 false,则 WaitOne 时阻塞等待;

/// <summary>
/// 测试任务进度弹窗
/// </summary>
private void BtnProgressDialog_Click(object sender, EventArgs e)
{
 _AutoResetEvent.Reset();
 string businessName = "业务1";

FormProgressDialog progressWindow = new FormProgressDialog()
 {
   Text = "任务处理窗口",
 };

progressWindow.SetColorfulTitle("业务1 ", Color.DarkOrange, true);
 progressWindow.SetColorfulTitle("正在执行中......", Color.Black);
 progressWindow.SetInfo(null, "", "");

List<string> orders = new List<string>(){"订单1", "订单2", "订单3", "订单4", "订单5" }; //业务数据;
 List<string> leftList = orders.Select(x => x).ToList(); //剩余(未处理)数据;
 int successCount = 0; //成功数量;

_Cts = new CancellationTokenSource();

//注册一个将在取消此 CancellationToken 时调用的委托;
 _Cts.Token.Register(async () =>
 {
   ShowInfo("操作终止");

await Task.Run(() =>
   {
     _AutoResetEvent.WaitOne(1000 * 5); //等待有可能还在执行的业务方法;

if (successCount < orders.Count)
     {
       ShowInfo($"{businessName} 有 {orders.Count - successCount} 项任务被终止,可在消息框中查看具体项。");

foreach (var leftName in leftList)
       {
         ShowInfo($"【{businessName}】的【{leftName}】执行失败,失败原因:【手动终止】。");
       }
     }
   });

});

progressWindow.OperateAction += () =>
 {
   Task task = new Task(() =>
   {
     foreach (var order in orders)
     {
       //判断是否被取消;
       if (_Cts.Token.IsCancellationRequested)
       {
         break;
       }

progressWindow.TryBeginInvoke(new Action(() =>
       {
         progressWindow.SetInfo(null, $"共{orders.Count}项,已执行{successCount}项", $"当前正在执行:{order}");
       }));

if (BusinessMethod(order, businessName))
       {
         successCount++;
         leftList.RemoveAll(x => x == order);

if (_Cts.Token.IsCancellationRequested)
         {
           _AutoResetEvent.Set(); //放行 Register 委托处的等待;
         }
       }

progressWindow.TryBeginInvoke(new Action(() =>
       {
         progressWindow.SetProsess(orders.IndexOf(order) + 1, orders.Count);
       }));
     }
   }, _Cts.Token);

task.Start();
   task.Wait();
 };

progressWindow.AbortAction += () =>
 {
   _Cts.Cancel();
 };

var result = progressWindow.ShowDialog();
 int leftCount = orders.Count - successCount;
 if (result == DialogResult.OK || leftCount <= 0)
 {
   ShowInfo($"{businessName} 整体完成。");
 }
 else if (result == DialogResult.Abort)
 {
   //移到 _Cts.Token.Register 处一起判断,不然数目可能不准;
   //ShowInfo($"{businessName} 有 {leftCount} 项任务被终止,可在消息框中查看具体项。");
 }
}

/// <summary>
/// 业务处理方法
/// </summary>
private bool BusinessMethod(string order, string businessName)
{
 string errStr = $"【{businessName}】的 {order} 任务失败,失败原因:";

//测试
 Thread.Sleep(1000 * 2);

try
 {
   //业务方法;

ShowInfo($"【{businessName}】的 {order} 任务执行成功。");
   return true;
 }
 catch (Exception ex)
 {
   ShowInfo($"{errStr}{ex.Message}");
 }

return false;
}

#endregion

四、效果展示和代码地址

正常执行(动图):

Winform 实现进度条弹窗和任务控制

中止执行(动图):

Winform 实现进度条弹窗和任务控制

代码地址:https://gitee.com/dlgcy/Practice/tree/master/WinFormPractice

转载自 独立观察员•博客

来源:http://dlgcy.com/winform-progress-dialog-and-task-control/

标签:Winform,进度条,弹窗,任务,控制
0
投稿

猜你喜欢

  • Java数据结构之顺序表的实现

    2023-06-22 00:47:26
  • Spring Boot整合流控组件Sentinel的场景分析

    2023-06-22 19:27:53
  • Spring MVC 关于controller的字符编码问题

    2023-06-17 09:52:52
  • C# GDI+实现时钟表盘

    2023-06-20 07:11:32
  • Flutter 通过Clipper实现各种自定义形状的示例代码

    2023-06-19 14:25:11
  • Spring源码解析之编程式事务

    2023-06-20 19:17:49
  • C++实现LeetCode(2.两个数字相加)

    2023-06-23 16:51:11
  • flutter轮子计划之进度条

    2023-06-21 07:59:39
  • Visual Studio 2022 安装低版本 .Net Framework的图文教程

    2023-06-22 19:18:44
  • SSH框架网上商城项目第2战之基本增删查改、Service和Action的抽取

    2023-06-21 19:16:23
  • OpenCV实现直线拟合

    2023-06-22 15:22:37
  • Java多线程之ThreadLocal浅析

    2023-06-19 19:55:37
  • 10种简单的Java性能优化

    2023-06-20 20:43:41
  • C#创建临时文件的方法

    2023-06-16 14:32:36
  • Flutter路由传递参数及解析实现

    2023-06-22 11:48:45
  • 自定义类加载器以及打破双亲委派模型解析

    2023-06-22 22:03:59
  • 详解Android Flutter中SliverAppBar的使用教程

    2023-06-23 12:11:27
  • 基于Flutter实现多边形和多角星组件

    2023-06-19 06:02:50
  • Flutter应用集成极光推送的实现示例

    2023-06-24 03:51:04
  • Opencv光流运动物体追踪详解

    2023-06-21 11:55:31
  • asp之家 软件编程 m.aspxhome.com