本来已经写完了,但我以前的一个同事说看了两遍都没怎么看懂我要说什么,受刺激了,只好推倒重写。
在WinForm开发中,耗时操作经常会使界面失去响应,比如查询数据、调用远端WebService等。为了不让界面失去响应,一般会采用多线程来处理或者是借助控件,比如BackgroundWorker,在Worker执行耗时操作时,界面上一般出于用户体验的考虑,还要加上等待提示,比如一个沙漏的图片或是类似“正在操作,请等待。。。。。。”的文字提示。
我写了个组件,针对以上情况进行了简单的封装,力求做到最简化,易于使用,使得在调用时尽量不需要考虑Thread、跨线程访问UI控件、等待提示的处理等等。
我把耗时操作分为了两种,一种是获取数据后绑定DataGridView,另一种是获取数据后进行其它处理,针对前者,在调用时只需要将获取数据的代码(以委托形式)和需要绑定的DataGridView控件一起传入组件即可,针对后者,需要将获取数据的代码(以委托形式)和获取数据后进行其它处理的代码(以委托形式)传入组件即可,需要说明的是,获取数据的代码应避免访问界面上的控件,获取数据后进行其它处理的代码由于是在主线程(UI线程)上跑所以可以自由访问界面控件。
我把提示又分为了两种,一种是组件自带的,组件会为当前窗口动态生成一个Label,用于提示之用,另一种是用户自定义的,用户如果觉得自带的提示实在不雅观,可以自己在界面上做一个Panel,然后传入组件,组件会在Worker执行时将其显示并在执行完毕后将其隐藏。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 using System;
2 using System.Collections.Generic;
3 using System.ComponentModel;
4 using System.Data;
5 using System.Drawing;
6 //using System.Linq;
7 using System.Text;
8 using System.Windows.Forms;
9
10 using System.Threading;
11
12 namespace DReBorn.Suede.Tests
13 {
14 public partial class WorkerHelperTests : Form
15 {
16 //使用前先在窗口类中声明、实例化这个组件的变量,WorkerHelper表明Worker中获取的数据是DataTable形式,WorkerHelper<T>表明Worker中获取的数据是List<T>形式。
17 private WorkerHelper worker = new WorkerHelper();
18 private WorkerHelper<Product> workerGenerics = new WorkerHelper<Product>();
19
20 private DataTable GetData()
21 {
22 DataTable dt = new DataTable();
23 dt.Columns.Add(new DataColumn("col1"));
24 dt.Columns.Add(new DataColumn("col2"));
25 dt.Columns.Add(new DataColumn("col3"));
26
27 for (int i = 1; i <= 100; i++)
28 {
29 DataRow dr = dt.NewRow();
30 dr["col1"] = i.ToString() + "col1";
31 dr["col2"] = i.ToString() + "col2";
32 dr["col3"] = i.ToString() + "col3";
33
34 dt.Rows.Add(dr);
35 }
36
37 return dt;
38 }
39
40 private List<Product> GetDataGenerics()
41 {
42 List<Product> list = new List<Product>();
43
44 for (int i = 1; i <= 100; i++)
45 {
46 Product stu = new Product();
47 stu.S1 = i.ToString() + "s1";
48 stu.S2 = i.ToString() + "s2";
49 stu.S3 = i.ToString() + "s3";
50
51 list.Add(stu);
52 }
53
54 return list;
55 }
56
57 public WorkerHelperTests()
58 {
59 InitializeComponent();
60 dataGridView1.AutoGenerateColumns = false;
61 }
62
63 private void button1_Click(object sender, EventArgs e)
64 {
65 //首先初始化(Init),这个主要是给传入的窗口加上提示控件。然后传入要在Worker中运行的逻辑和获取数据完毕后绑定的DataGridView。
66 worker.Init(this);
67 worker.Action(() => {
68 DataTable dt = GetData();
69 Thread.Sleep(1000 * 10);
70 worker.DataSource = dt;
71 }, dataGridView1);
72 }
73
74 private void button2_Click(object sender, EventArgs e)
75 {
76 //这是另外一种调用形式,如果嫌自动生成的提示实在不美观,可以自己写个Panel,里面放上自己满意的内容,然后指定它来作为提示控件。
77 worker.Init(this, panel1);
78 worker.Action(() => {
79 DataTable dt = GetData();
80 Thread.Sleep(1000 * 10);
81 worker.DataSource = dt;
82 }, dataGridView1);
83 }
84
85 private void button3_Click(object sender, EventArgs e)
86 {
87 //这种调用形式,主要是在获取数据后,不进行绑定,而要进行其它的操作,比如获取数据后导出为一份Excel文档等。
88 worker.Action(() => {
89 DataTable dt = GetData();
90 Thread.Sleep(1000 * 10);
91 worker.DataSource = dt;
92 }, () => {
93 MessageBox.Show(worker.DataSource.Rows.Count.ToString());
94 });
95 }
96
97 private void button6_Click(object sender, EventArgs e)
98 {
99 workerGenerics.Init(this);
100 workerGenerics.Action(() => {
101 Thread.Sleep(1000 * 10);
102 workerGenerics.DataSource = GetDataGenerics();
103 }, dataGridView2);
104 }
105
106 private void button5_Click(object sender, EventArgs e)
107 {
108 workerGenerics.Init(this, panel2);
109 workerGenerics.Action(() => {
110 Thread.Sleep(1000 * 10);
111 workerGenerics.DataSource = GetDataGenerics();
112 }, dataGridView2);
113 }
114
115 private void button4_Click(object sender, EventArgs e)
116 {
117 workerGenerics.Action(() => {
118 Thread.Sleep(1000 * 10);
119 workerGenerics.DataSource = GetDataGenerics();
120 }, () => {
121 MessageBox.Show(workerGenerics.DataSource[0].S1);
122 });
123 }
124 }
125
126 public class Product
127 {
128 public string S1 { get; set; }
129 public string S2 { get; set; }
130 public string S3 { get; set; }
131 }
132 }
以上为获取数据是DataTable形式的情况,至于泛型版本就不赘述了,感兴趣的童鞋可以看看源码,Very Simple。上截图。
自动生成的提示控件
自定义的Panel作为提示控件(粉色区域)
获取数据后进行自定义操作(此例中为报告行数)。