最近要给一个 Winform 项目添加功能,需要一个能显示进度条的弹窗,还要求能够中止任务,所以就做了一个,在此做个记录总结。虽然用的是比较老的 Winform 技术,不过其中的原理都是相通的。
一、弹窗前台
首先提供一个 Winform 控件居中的小技巧:
将控件放在 TableLayoutPanel 容器中,然后将控件的 Anchor 属性设置为 None,这样控件就能在容器中居中了:
将容器的 Anchor 属性设置为 Top, Left, Right,这样容器就能随着窗口左右拉伸了:
最终弹窗界面如下:
使用了 CSkin 界面库(v16.1.14.3),(注意:如果拖拽 dll 到工具箱拖不了,可以使用右键复制粘贴的方式),窗体继承 Skin_DevExpress,进度条使用 SkinProgressBar,按钮使用 SkinButton,主要是使用了一些圆角效果:
二、弹窗后台
先添加两个事件供外界订阅,分别为窗体载入时触发的执行操作事件,和点击中止按钮后触发的终止操作事件:
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/// <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。
供外界设置文本信息以及进度条进度的方法如下:
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/// <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 对象(用于取消任务后的一些信息同步);然后是主测试方法(一个按钮点击事件方法)中的一些信息设置:
然后设置 CancellationTokenSource 对象的 Token,给它注册一个取消任务时调用的委托方法,里面先等待同步信号结果再进行本次执行结果的判断:
接下来订阅弹窗中的那两个事件,在执行操作事件中开启任务,并传递 Token;在中止事件中停止任务:
需要注意的是,停止任务后,任务内部并不会自己停止,需要判断 Token 的 IsCancellationRequested 字段来决定相应的操作,比如结束循环。然后,因为在之前注册的取消的委托方法中,进行了等待,所以我们在执行完业务方法(BusinessMethod)并设置好相关状态值后,需要判断任务是否取消,如果取消,说明注册的取消的委托方法中已经在等待了,所以要调用 Set () 进行放行。
有人可能就会问了,foreach 循环开始时不是判断过是否取消了吗?这里怎么又判断?这是因为,比如在一轮循环中,已经执行过了开头的是否已取消的判断(IsCancellationRequested 为 false),开始执行耗时的业务方法了,此时用户点击中止按钮,IsCancellationRequested 被置为 true,所以业务方法执行后再次判断会得到最新的状态,然后,循环将在下一轮开始时结束。
另外,由于实际使用这个的项目是 .NET 4.0 框架,所以 Task 的一些方法没有,大家用新框架的话可以使用新方法。或者使用 Microsoft.Bcl.Async 包,然后使用 TaskEx。
继续流程,接下来以模态框方式弹出窗口,并获取结果。业务处理方法中模拟了耗时操作并返回是否成功。
最后给出完整代码:
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133#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
四、效果展示和代码地址
正常执行(动图):
中止执行(动图):
代码地址:https://gitee.com/dlgcy/Practice/tree/master/WinFormPractice
转载自 独立观察员•博客
以上就是Winform 实现进度条弹窗和任务控制的详细内容,更多关于Winform 进度条弹窗和任务控制的资料请关注靠谱客其它相关文章!
最后
以上就是超级金鱼最近收集整理的关于Winform 实现进度条弹窗和任务控制的全部内容,更多相关Winform内容请搜索靠谱客的其他文章。
发表评论 取消回复