目录
概述
取消跨线程检查
使用委托异步调用
sync和await
总结
概述
最近在qq群里有一朋友,问起在winform中怎么通过开启线程的方式去处理耗时的操作,比如,查看某个目录下所有的文件,或者符合要求的文件。下班回来,也研究了一下。发现多线程这块有点薄弱,也算是补一补吧。
在winform开发,经常会遇到需要在控件上加载大量数据(也就是常说的耗时操作),这会导致程序出现假死状态,这个时候我们就会想到线程。
在智能客户端应用程序中,这样的线程创建并管理用户界面 (UI),因而称为 UI 线程。
可以将 UI 线程用于所有的处理,其中包括 Web 服务调用、远程对象调用和数据库调用。然而,以这种方式使用 UI 线程通常并不是一个好主意。在大多数情况下,您不能预测调用Web 服务、远程对象或数据库会持续多久,而且在 UI 线程等待响应时,可能会导致 UI 冻结。
通过创建附加线程,应用程序可以在不使用 UI 线程的情况下执行额外的处理。当应用程序调用 Web 服务时,可以使用多线程来防止 UI 冻结或并行执行某些本地任务,以整体提高应用程序的效率。在大多数情况下,您应该坚持在单独的线程上执行任何与 UI 无关的任务。
取消跨线程检查
案例:现在做一个这样的测试项目,我们选择一个目录通过递归的方式,遍历所有的文件,将文件信息,加载到窗体的DataGridView控件上。界面如图所示:
代码
事件参数和委托:
01.
1
using System;
02.
2
using System.Collections.Generic;
03.
3
using System.Linq;
04.
4
using System.Text;
05.
5
using System.Threading.Tasks;
06.
6
07.
7
namespace Wofy.ThreadDemo
08.
8
{
09.
9
10.
10
/// <summary>
11.
11
///功能描述 : 事件参数
12.
12
///开发者 : wolfy
13.
13
///建立时间 : 2014年07月19日
14.
14
///修订描述 :
15.
15
///进度描述 :
16.
16
///版本号 : 1.0
17.
17
///最后修改时间: 2014年07月19日
18.
18
/// </summary>
19.
19
public
class
FileMessageEventArgs:EventArgs
20.
20
{
21.
21
public
FileMessage fileMessage{set;get;}
22.
22
}
23.
23
}
01.
1
using System;
02.
2
using System.Collections.Generic;
03.
3
using System.Linq;
04.
4
using System.Text;
05.
5
using System.Threading.Tasks;
06.
6
07.
7
namespace Wofy.ThreadDemo
08.
8
{
09.
9
10.
10
/// <summary>
11.
11
///功能描述 : 文件信息委托
12.
12
///开发者 : wolfy
13.
13
///建立时间 : 2014年07月19日
14.
14
///修订描述 :
15.
15
///进度描述 :
16.
16
///版本号 : 1.0
17.
17
///最后修改时间: 2014年07月19日
18.
18
/// </summary>
19.
19
public
delegate
void
FileMessageEventHandler(object sender, FileMessageEventArgs e);
20.
20
21.
21
}
文件信息类:
01.
1
using System;
02.
2
using System.Collections.Generic;
03.
3
using System.ComponentModel;
04.
4
using System.Linq;
05.
5
using System.Text;
06.
6
using System.Threading.Tasks;
07.
7
08.
8
namespace Wofy.ThreadDemo
09.
9
{
10.
10
/// <summary>
11.
11
/// 文件信息
12.
12
/// </summary>
13.
13
public
class
FileMessage
14.
14
{
15.
15
/// <summary>
16.
16
/// 序号
17.
17
/// </summary>
18.
18
[Description(
'序号'
)]
19.
19
public
int
intCount { get; set; }
20.
20
/// <summary>
21.
21
/// 文件路径
22.
22
/// </summary>
23.
23
[Description(
'文件路径'
)]
24.
24
public
string strFilePath { set; get; }
25.
25
/// <summary>
26.
26
/// 文件名
27.
27
/// </summary>
28.
28
[Description(
'文件名'
)]
29.
29
public
string strFileName { set; get; }
30.
30
/// <summary>
31.
31
/// 文件类型
32.
32
/// </summary>
33.
33
[Description(
'文件类型'
)]
34.
34
public
string strFileType { set; get; }
35.
35
}
36.
36
}
窗体代码:
001.
1
using System;
002.
2
using System.Collections.Generic;
003.
3
using System.ComponentModel;
004.
4
using System.Data;
005.
5
using System.Drawing;
006.
6
using System.Linq;
007.
7
using System.Reflection;
008.
8
using System.Text;
009.
9
using System.Threading.Tasks;
010.
10
using System.Windows.Forms;
011.
11
using System.IO;
012.
12
using System.Threading;
013.
13
namespace Wofy.ThreadDemo
014.
14
{
015.
15
/// <summary>
016.
16
///功能描述 : 文件<a href="http://www.it165.net/edu/ewl/" target="_blank" class="keylink">浏览器</a>主窗口
017.
17
///开发者 : wolfy
018.
18
///建立时间 : 2014年07月19日
019.
19
///修订描述 :
020.
20
///进度描述 :
021.
21
///版本号 : 1.0
022.
22
///最后修改时间: 2014年07月19日
023.
23
/// </summary>
024.
24
public
partial
class
MainForm : Form
025.
25
{
026.
26
public
MainForm()
027.
27
{
028.
28
InitializeComponent();
029.
29
//取消跨线程检查
030.
30
// Form.CheckForIllegalCrossThreadCalls = false;
031.
31
}
032.
32
private
event FileMessageEventHandler fileMessageEventHandler;
033.
33
private
void
btnSelectPath_Click(object sender, EventArgs e)
034.
34
{
035.
35
FolderBrowserDialog folderBrowserDialog =
new
FolderBrowserDialog();
036.
36
if
(folderBrowserDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
037.
37
{
038.
38
//目录路径
039.
39
this
.txtPath.Text = folderBrowserDialog.SelectedPath;
040.
40
fileMessageEventHandler += MainForm_fileMessageEventHandler;
041.
41
Thread thread =
new
Thread(
new
ParameterizedThreadStart(GetFiles));
042.
42
thread.IsBackground =
true
;
043.
43
thread.Start(
this
.txtPath.Text);
044.
44
}
045.
45
046.
46
}
047.
47
/// <summary>
048.
48
/// 文件信息事件处理
049.
49
/// </summary>
050.
50
/// <param name='sender'></param>
051.
51
/// <param name='e'></param>
052.
52
void
MainForm_fileMessageEventHandler(object sender, FileMessageEventArgs e)
053.
53
{
054.
54
FileMessage fileMessage = e.fileMessage;
055.
55
this
.dgViewFiles.Rows.Add(
new
object[] { fileMessage.intCount, fileMessage.strFilePath, fileMessage.strFileName, fileMessage.strFileType });
056.
56
}
057.
57
058.
58
private
List<string> lstTypes =
null
;
059.
59
static
object _objLock =
new
object();
060.
60
int
intFileCount =
1
;
061.
61
/// <summary>
062.
62
/// 递归获得文件信息
063.
63
/// </summary>
064.
64
/// <param name='strPath'></param>
065.
65
/// <returns></returns>
066.
66
public
void
GetFiles(object obj)
067.
67
{
068.
68
string strPath = obj.ToString();
069.
69
List<FileMessage> lstFiles =
new
List<FileMessage>();
070.
70
071.
71
//单例创建集合
072.
72
if
(lstTypes ==
null
)
073.
73
{
074.
74
lock (_objLock)
075.
75
{
076.
76
if
(lstTypes ==
null
)
077.
77
{
078.
78
lstTypes = GetCheckedFileType();
079.
79
}
080.
80
}
081.
81
}
082.
82
string[] files =
new
string[
0
];
083.
83
if
(lstTypes.Count >
0
)
084.
84
{
085.
85
foreach (string strType in lstTypes)
086.
86
{
087.
87
files = Directory.GetFiles(strPath,
'*'
+ strType);
088.
88
AddFileMessage(files);
089.
89
}
090.
90
}
091.
91
else
092.
92
{
093.
93
files = Directory.GetFiles(strPath);
094.
94
AddFileMessage(files);
095.
95
}
096.
96
string[] strDirs = Directory.GetDirectories(strPath);
097.
97
for
(
int
i =
0
; i < strDirs.Length; i++)
098.
98
{
099.
99
GetFiles(strDirs[i]);
100.
100
}
101.
101
}
102.
102
/// <summary>
103.
103
/// 将信息添加到集合
104.
104
/// </summary>
105.
105
/// <param name='files'></param>
106.
106
private
void
AddFileMessage(string[] files)
107.
107
{
108.
108
for
(
int
i =
0
; i < files.Length; i++)
109.
109
{
110.
110
FileInfo fileInfo =
new
FileInfo(files[i]);
111.
111
FileMessage fileMessage =
new
FileMessage();
112.
112
fileMessage.intCount = intFileCount++;
113.
113
fileMessage.strFileName = Path.GetFileName(fileInfo.FullName);
114.
114
fileMessage.strFilePath = fileInfo.FullName;
115.
115
fileMessage.strFileType = fileInfo.Extension;
116.
116
FileMessageEventArgs e =
new
FileMessageEventArgs();
117.
117
e.fileMessage = fileMessage;
118.
118
this
.fileMessageEventHandler(
null
, e);
119.
119
}
120.
120
}
121.
121
/// <summary>
122.
122
/// 获得选择的文件类型
123.
123
/// </summary>
124.
124
/// <returns></returns>
125.
125
private
List<string> GetCheckedFileType()
126.
126
{
127.
127
List<string> lstFileTypes =
new
List<string>();
128.
128
foreach (Control control in
this
.Controls)
129.
129
{
130.
130
if
(control is CheckBox)
131.
131
{
132.
132
CheckBox checkBox = control as CheckBox;
133.
133
if
(checkBox !=
null
&& checkBox.Checked)
134.
134
{
135.
135
lstFileTypes.Add(checkBox.Text);
136.
136
}
137.
137
}
138.
138
}
139.
139
return
lstFileTypes;
140.
140
}
141.
141
/// <summary>
142.
142
/// 窗体加载
143.
143
/// </summary>
144.
144
/// <param name='sender'></param>
145.
145
/// <param name='e'></param>
146.
146
private
void
MainForm_Load(object sender, EventArgs e)
147.
147
{
148.
148
//通过反射的方式添加列
149.
149
Type type = typeof(FileMessage);
150.
150
PropertyInfo[] propertyInfos = type.GetProperties();
151.
151
foreach (PropertyInfo propertyInfo in propertyInfos)
152.
152
{
153.
153
object[] objs = propertyInfo.GetCustomAttributes(typeof(DescriptionAttribute),
true
);
154.
154
if
(objs.Length >
0
)
155.
155
{
156.
156
DescriptionAttribute attr = objs[
0
] as DescriptionAttribute;
157.
157
string result = attr.Description;
158.
158
this
.dgViewFiles.Columns.Add(result, result);
159.
159
}
160.
160
}
161.
161
//调整列宽
162.
162
AutoSizeColumn(dgViewFiles);
163.
163
164.
164
165.
165
}
166.
166
/// <summary>
167.
167
/// 使DataGridView的列自适应宽度
168.
168
/// </summary>
169.
169
/// <param name='dgViewFiles'></param>
170.
170
private
void
AutoSizeColumn(DataGridView dgViewFiles)
171.
171
{
172.
172
int
width =
0
;
173.
173
//使列自使用宽度
174.
174
//对于DataGridView的每一个列都调整
175.
175
for
(
int
i =
0
; i < dgViewFiles.Columns.Count; i++)
176.
176
{
177.
177
//将每一列都调整为自动适应模式
178.
178
dgViewFiles.AutoResizeColumn(i, DataGridViewAutoSizeColumnMode.AllCells);
179.
179
//记录整个DataGridView的宽度
180.
180
width += dgViewFiles.Columns[i].Width;
181.
181
}
182.
182
//判断调整后的宽度与原来设定的宽度的关系,如果是调整后的宽度大于原来设定的宽度,
183.
183
//则将DataGridView的列自动调整模式设置为显示的列即可,
184.
184
//如果是小于原来设定的宽度,将模式改为填充。
185.
185
if
(width > dgViewFiles.Size.Width)
186.
186
{
187.
187
dgViewFiles.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells;
188.
188
}
189.
189
else
190.
190
{
191.
191
dgViewFiles.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
192.
192
}
193.
193
//冻结某列 从左开始 0,1,2
194.
194
dgViewFiles.Columns[
1
].Frozen =
true
;
195.
195
}
196.
196
}
197.
197
}
如果上面的代码会报错:
出现这个错误,是因为新开的线程操作UI主线程上的控件导致的。也就有了第一种解决方案,添加如下代码即可解决问题:
1.
1
//取消跨线程检查
2.
2
Control.CheckForIllegalCrossThreadCalls =
false
;
取消跨线程检测,总感觉心里不爽,它们是线程安全的,这里非得强制去取消,总感觉有什么隐患似的。虽然解决了问题,但是对DataGridView滚动条却无法使用了。这里就有了常规使用的第二种方案,通过委托来实现。
使用委托异步调用
使用委托修改原来的代码:
001.
1
using System;
002.
2
using System.Collections.Generic;
003.
3
using System.ComponentModel;
004.
4
using System.Data;
005.
5
using System.Drawing;
006.
6
using System.Linq;
007.
7
using System.Reflection;
008.
8
using System.Text;
009.
9
using System.Threading.Tasks;
010.
10
using System.Windows.Forms;
011.
11
using System.IO;
012.
12
using System.Threading;
013.
13
namespace Wofy.ThreadDemo
014.
14
{
015.
15
/// <summary>
016.
16
///功能描述 : 文件<a href="http://www.it165.net/edu/ewl/" target="_blank" class="keylink">浏览器</a>主窗口
017.
17
///开发者 : wolfy
018.
18
///建立时间 : 2014年07月19日
019.
19
///修订描述 :
020.
20
///进度描述 :
021.
21
///版本号 : 1.0
022.
22
///最后修改时间: 2014年07月19日
023.
23
/// </summary>
024.
24
public
partial
class
MainForm : Form
025.
25
{
026.
26
public
MainForm()
027.
27
{
028.
28
InitializeComponent();
029.
29
}
030.
30
private
event FileMessageEventHandler fileMessageEventHandler;
031.
31
Thread thread;
032.
32
private
void
btnSelectPath_Click(object sender, EventArgs e)
033.
33
{
034.
34
FolderBrowserDialog folderBrowserDialog =
new
FolderBrowserDialog();
035.
35
if
(folderBrowserDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
036.
36
{
037.
37
//目录路径
038.
38
this
.txtPath.Text = folderBrowserDialog.SelectedPath;
039.
39
fileMessageEventHandler += MainForm_fileMessageEventHandler;
040.
40
thread =
new
Thread(
new
ParameterizedThreadStart(GetFiles));
041.
41
thread.IsBackground =
true
;
042.
42
thread.Start(
this
.txtPath.Text);
043.
43
}
044.
44
045.
45
}
046.
46
//委托
047.
47
private
delegate
void
DelegateSetDataGridView(FileMessage fileMessage);
048.
48
/// <summary>
049.
49
///
050.
50
/// </summary>
051.
51
/// <param name='fileMessage'></param>
052.
52
private
void
SetDataGridView(FileMessage fileMessage)
053.
53
{
054.
54
//获取一个值,该值指示调用方在对控件进行方法调用时是否必须调用 Invoke 方法,因为调用方位于创建控件所在的线程以外的线程中。
055.
55
if
(
this
.dgViewFiles.InvokeRequired)
056.
56
{
057.
57
Invoke(
new
DelegateSetDataGridView(SetDataGridView),
new
object[] { fileMessage });
058.
58
}
059.
59
else
060.
60
{
061.
61
this
.dgViewFiles.Rows.Add(
new
object[] { fileMessage.intCount, fileMessage.strFilePath, fileMessage.strFileName, fileMessage.strFileType });
062.
62
}
063.
63
064.
64
}
065.
65
066.
66
/// <summary>
067.
67
/// 文件信息事件处理
068.
68
/// </summary>
069.
69
/// <param name='sender'></param>
070.
70
/// <param name='e'></param>
071.
71
void
MainForm_fileMessageEventHandler(object sender, FileMessageEventArgs e)
072.
72
{
073.
73
FileMessage fileMessage = e.fileMessage;
074.
74
SetDataGridView(fileMessage);
075.
75
}
076.
76
077.
77
private
List<string> lstTypes =
null
;
078.
78
static
object _objLock =
new
object();
079.
79
int
intFileCount =
1
;
080.
80
/// <summary>
081.
81
/// 递归获得文件信息
082.
82
/// </summary>
083.
83
/// <param name='strPath'></param>
084.
84
/// <returns></returns>
085.
85
public
void
GetFiles(object obj)
086.
86
{
087.
87
string strPath = obj.ToString();
088.
88
List<FileMessage> lstFiles =
new
List<FileMessage>();
089.
89
090.
90
//单例创建集合
091.
91
if
(lstTypes ==
null
)
092.
92
{
093.
93
lock (_objLock)
094.
94
{
095.
95
if
(lstTypes ==
null
)
096.
96
{
097.
97
lstTypes = GetCheckedFileType();
098.
98
}
099.
99
}
100.
100
}
101.
101
string[] files =
new
string[
0
];
102.
102
if
(lstTypes.Count >
0
)
103.
103
{
104.
104
foreach (string strType in lstTypes)
105.
105
{
106.
106
files = Directory.GetFiles(strPath,
'*'
+ strType);
107.
107
AddFileMessage(files);
108.
108
}
109.
109
}
110.
110
else
111.
111
{
112.
112
files = Directory.GetFiles(strPath);
113.
113
AddFileMessage(files);
114.
114
}
115.
115
string[] strDirs = Directory.GetDirectories(strPath);
116.
116
for
(
int
i =
0
; i < strDirs.Length; i++)
117.
117
{
118.
118
GetFiles(strDirs[i]);
119.
119
}
120.
120
}
121.
121
/// <summary>
122.
122
/// 将信息添加到集合
123.
123
/// </summary>
124.
124
/// <param name='files'></param>
125.
125
private
void
AddFileMessage(string[] files)
126.
126
{
127.
127
for
(
int
i =
0
; i < files.Length; i++)
128.
128
{
129.
129
FileInfo fileInfo =
new
FileInfo(files[i]);
130.
130
FileMessage fileMessage =
new
FileMessage();
131.
131
fileMessage.intCount = intFileCount++;
132.
132
fileMessage.strFileName = Path.GetFileName(fileInfo.FullName);
133.
133
fileMessage.strFilePath = fileInfo.FullName;
134.
134
fileMessage.strFileType = fileInfo.Extension;
135.
135
FileMessageEventArgs e =
new
FileMessageEventArgs();
136.
136
e.fileMessage = fileMessage;
137.
137
this
.fileMessageEventHandler(
null
, e);
138.
138
}
139.
139
}
140.
140
/// <summary>
141.
141
/// 获得选择的文件类型
142.
142
/// </summary>
143.
143
/// <returns></returns>
144.
144
private
List<string> GetCheckedFileType()
145.
145
{
146.
146
List<string> lstFileTypes =
new
List<string>();
147.
147
foreach (Control control in
this
.Controls)
148.
148
{
149.
149
if
(control is CheckBox)
150.
150
{
151.
151
CheckBox checkBox = control as CheckBox;
152.
152
if
(checkBox !=
null
&& checkBox.Checked)
153.
153
{
154.
154
lstFileTypes.Add(checkBox.Text);
155.
155
}
156.
156
}
157.
157
}
158.
158
return
lstFileTypes;
159.
159
}
160.
160
/// <summary>
161.
161
/// 窗体加载
162.
162
/// </summary>
163.
163
/// <param name='sender'></param>
164.
164
/// <param name='e'></param>
165.
165
private
void
MainForm_Load(object sender, EventArgs e)
166.
166
{
167.
167
//通过反射的方式添加列
168.
168
Type type = typeof(FileMessage);
169.
169
PropertyInfo[] propertyInfos = type.GetProperties();
170.
170
foreach (PropertyInfo propertyInfo in propertyInfos)
171.
171
{
172.
172
object[] objs = propertyInfo.GetCustomAttributes(typeof(DescriptionAttribute),
true
);
173.
173
if
(objs.Length >
0
)
174.
174
{
175.
175
DescriptionAttribute attr = objs[
0
] as DescriptionAttribute;
176.
176
string result = attr.Description;
177.
177
this
.dgViewFiles.Columns.Add(result, result);
178.
178
}
179.
179
}
180.
180
//调整列宽
181.
181
AutoSizeColumn(dgViewFiles);
182.
182
183.
183
184.
184
}
185.
185
/// <summary>
186.
186
/// 使DataGridView的列自适应宽度
187.
187
/// </summary>
188.
188
/// <param name='dgViewFiles'></param>
189.
189
private
void
AutoSizeColumn(DataGridView dgViewFiles)
190.
190
{
191.
191
int
width =
0
;
192.
192
//使列自使用宽度
193.
193
//对于DataGridView的每一个列都调整
194.
194
for
(
int
i =
0
; i < dgViewFiles.Columns.Count; i++)
195.
195
{
196.
196
//将每一列都调整为自动适应模式
197.
197
dgViewFiles.AutoResizeColumn(i, DataGridViewAutoSizeColumnMode.AllCells);
198.
198
//记录整个DataGridView的宽度
199.
199
width += dgViewFiles.Columns[i].Width;
200.
200
}
201.
201
//判断调整后的宽度与原来设定的宽度的关系,如果是调整后的宽度大于原来设定的宽度,
202.
202
//则将DataGridView的列自动调整模式设置为显示的列即可,
203.
203
//如果是小于原来设定的宽度,将模式改为填充。
204.
204
if
(width > dgViewFiles.Size.Width)
205.
205
{
206.
206
dgViewFiles.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells;
207.
207
}
208.
208
else
209.
209
{
210.
210
dgViewFiles.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
211.
211
}
212.
212
//冻结某列 从左开始 0,1,2
213.
213
dgViewFiles.Columns[
1
].Frozen =
true
;
214.
214
}
215.
215
216.
216
private
void
MainForm_FormClosing(object sender, FormClosingEventArgs e)
217.
217
{
218.
218
//窗体关闭是停止线程
219.
219
thread.Abort();
220.
220
}
221.
221
}
222.
222
}
关于Control.Invoke可以参考下面的文章:
http://msdn.microsoft.com/zh-CN/library/system.windows.forms.control.invoke.aspx
http://msdn.microsoft.com/zh-cn/library/zyzhdc6b.aspx
关于Control.InvokeRequire可以参考下面的文章:
http://msdn.microsoft.com/zh-cn/library/system.windows.forms.control.invokerequired.aspx
Windows 窗体中的控件被绑定到特定的线程,不具备线程安全性。 因此,如果从另一个线程调用控件的方法,那么必须使用控件的一个 Invoke 方法来将调用封送到适当的线程。 该属性可用于确定是否必须调用 Invoke 方法,当不知道什么线程拥有控件时这很有用。
窗体上的控件只允许创建它们的线程访问,也就是主线程,如果非主线程访问则会发生异常。我们可以借助于控件的InvokeRequired属性来判断该控件目前是否被主线程访问,如果是,返回false。如果不是,再利用Invoke方法找到主线程,让主线程执行访问控件的方法。
async和await
之前在博客园看到async和await方面的文章,就想着使用一下,发现异步加载变得如此简单。
关于async和await可参考
http://www.cnblogs.com/jesse2013/p/async-and-await.html
使用async和await改写上面的代码:
001.
1
using System;
002.
2
using System.Collections.Generic;
003.
3
using System.ComponentModel;
004.
4
using System.Data;
005.
5
using System.Drawing;
006.
6
using System.Linq;
007.
7
using System.Reflection;
008.
8
using System.Text;
009.
9
using System.Threading.Tasks;
010.
10
using System.Windows.Forms;
011.
11
using System.IO;
012.
12
using System.Threading;
013.
13
namespace Wofy.ThreadDemo
014.
14
{
015.
15
/// <summary>
016.
16
///功能描述 : 文件浏览器主窗口
017.
17
///开发者 : wolfy
018.
18
///建立时间 : 2014年07月19日
019.
19
///修订描述 :
020.
20
///进度描述 :
021.
21
///版本号 : 1.0
022.
22
///最后修改时间: 2014年07月19日
023.
23
/// </summary>
024.
24
public
partial
class
MainForm : Form
025.
25
{
026.
26
public
MainForm()
027.
27
{
028.
28
InitializeComponent();
029.
29
}
030.
30
private
event FileMessageEventHandler fileMessageEventHandler;
031.
31
//Thread thread;
032.
32
//注意加上async
033.
33
private
async
void
btnSelectPath_Click(object sender, EventArgs e)
034.
34
{
035.
35
FolderBrowserDialog folderBrowserDialog =
new
FolderBrowserDialog();
036.
36
if
(folderBrowserDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
037.
37
{
038.
38
//目录路径
039.
39
this
.txtPath.Text = folderBrowserDialog.SelectedPath;
040.
40
fileMessageEventHandler += MainForm_fileMessageEventHandler;
041.
41
await GetFiles(
this
.txtPath.Text);
042.
42
043.
43
//thread = new Thread(new ParameterizedThreadStart(GetFiles));
044.
44
//thread.IsBackground = true;
045.
45
//thread.Start(this.txtPath.Text);
046.
46
047.
47
}
048.
48
049.
49
}
050.
50
//委托
051.
51
private
delegate
void
DelegateSetDataGridView(FileMessage fileMessage);
052.
52
/// <summary>
053.
53
///
054.
54
/// </summary>
055.
55
/// <param name='fileMessage'></param>
056.
56
private
void
SetDataGridView(FileMessage fileMessage)
057.
57
{
058.
58
//获取一个值,该值指示调用方在对控件进行方法调用时是否必须调用 Invoke 方法,因为调用方位于创建控件所在的线程以外的线程中。
059.
59
if
(
this
.dgViewFiles.InvokeRequired)
060.
60
{
061.
61
Invoke(
new
DelegateSetDataGridView(SetDataGridView),
new
object[] { fileMessage });
062.
62
}
063.
63
else
064.
64
{
065.
65
this
.dgViewFiles.Rows.Add(
new
object[] { fileMessage.intCount, fileMessage.strFilePath, fileMessage.strFileName, fileMessage.strFileType });
066.
66
}
067.
67
068.
68
}
069.
69
070.
70
/// <summary>
071.
71
/// 文件信息事件处理
072.
72
/// </summary>
073.
73
/// <param name='sender'></param>
074.
74
/// <param name='e'></param>
075.
75
void
MainForm_fileMessageEventHandler(object sender, FileMessageEventArgs e)
076.
76
{
077.
77
FileMessage fileMessage = e.fileMessage;
078.
78
// SetDataGridView(fileMessage);
079.
79
this
.dgViewFiles.Rows.Add(
new
object[] { fileMessage.intCount, fileMessage.strFilePath, fileMessage.strFileName, fileMessage.strFileType });
080.
80
}
081.
81
082.
82
private
List<string> lstTypes =
null
;
083.
83
static
object _objLock =
new
object();
084.
84
int
intFileCount =
1
;
085.
85
/// <summary>
086.
86
/// 递归获得文件信息
087.
87
/// </summary>
088.
88
/// <param name='strPath'></param>
089.
89
/// <returns></returns>
090.
90
public
async Task<List<FileMessage>> GetFiles(object obj)
091.
91
{
092.
92
string strPath = obj.ToString();
093.
93
List<FileMessage> lstFiles =
new
List<FileMessage>();
094.
94
095.
95
//单例创建集合
096.
96
if
(lstTypes ==
null
)
097.
97
{
098.
98
lock (_objLock)
099.
99
{
100.
100
if
(lstTypes ==
null
)
101.
101
{
102.
102
lstTypes = GetCheckedFileType();
103.
103
}
104.
104
}
105.
105
}
106.
106
string[] files =
new
string[
0
];
107.
107
if
(lstTypes.Count >
0
)
108.
108
{
109.
109
foreach (string strType in lstTypes)
110.
110
{
111.
111
files = Directory.GetFiles(strPath,
'*'
+ strType);
112.
112
AddFileMessage(files);
113.
113
}
114.
114
}
115.
115
else
116.
116
{
117.
117
files = Directory.GetFiles(strPath);
118.
118
AddFileMessage(files);
119.
119
}
120.
120
string[] strDirs = Directory.GetDirectories(strPath);
121.
121
for
(
int
i =
0
; i < strDirs.Length; i++)
122.
122
{
123.
123
await GetFiles(strDirs[i]);
124.
124
}
125.
125
//创建Task,创建一个新的线程,不然还会出现UI假死的现象
126.
126
return
await Task.Run(() => {
return
lstFiles; });
127.
127
128.
128
}
129.
129
/// <summary>
130.
130
/// 将信息添加到集合
131.
131
/// </summary>
132.
132
/// <param name='files'></param>
133.
133
private
void
AddFileMessage(string[] files)
134.
134
{
135.
135
for
(
int
i =
0
; i < files.Length; i++)
136.
136
{
137.
137
FileInfo fileInfo =
new
FileInfo(files[i]);
138.
138
FileMessage fileMessage =
new
FileMessage();
139.
139
fileMessage.intCount = intFileCount++;
140.
140
fileMessage.strFileName = Path.GetFileName(fileInfo.FullName);
141.
141
fileMessage.strFilePath = fileInfo.FullName;
142.
142
fileMessage.strFileType = fileInfo.Extension;
143.
143
FileMessageEventArgs e =
new
FileMessageEventArgs();
144.
144
e.fileMessage = fileMessage;
145.
145
this
.fileMessageEventHandler(
null
, e);
146.
146
}
147.
147
}
148.
148
/// <summary>
149.
149
/// 获得选择的文件类型
150.
150
/// </summary>
151.
151
/// <returns></returns>
152.
152
private
List<string> GetCheckedFileType()
153.
153
{
154.
154
List<string> lstFileTypes =
new
List<string>();
155.
155
foreach (Control control in
this
.Controls)
156.
156
{
157.
157
if
(control is CheckBox)
158.
158
{
159.
159
CheckBox checkBox = control as CheckBox;
160.
160
if
(checkBox !=
null
&& checkBox.Checked)
161.
161
{
162.
162
lstFileTypes.Add(checkBox.Text);
163.
163
}
164.
164
}
165.
165
}
166.
166
return
lstFileTypes;
167.
167
}
168.
168
/// <summary>
169.
169
/// 窗体加载
170.
170
/// </summary>
171.
171
/// <param name='sender'></param>
172.
172
/// <param name='e'></param>
173.
173
private
void
MainForm_Load(object sender, EventArgs e)
174.
174
{
175.
175
//通过反射的方式添加列
176.
176
Type type = typeof(FileMessage);
177.
177
PropertyInfo[] propertyInfos = type.GetProperties();
178.
178
foreach (PropertyInfo propertyInfo in propertyInfos)
179.
179
{
180.
180
object[] objs = propertyInfo.GetCustomAttributes(typeof(DescriptionAttribute),
true
);
181.
181
if
(objs.Length >
0
)
182.
182
{
183.
183
DescriptionAttribute attr = objs[
0
] as DescriptionAttribute;
184.
184
string result = attr.Description;
185.
185
this
.dgViewFiles.Columns.Add(result, result);
186.
186
}
187.
187
}
188.
188
//调整列宽
189.
189
AutoSizeColumn(dgViewFiles);
190.
190
191.
191
192.
192
}
193.
193
/// <summary>
194.
194
/// 使DataGridView的列自适应宽度
195.
195
/// </summary>
196.
196
/// <param name='dgViewFiles'></param>
197.
197
private
void
AutoSizeColumn(DataGridView dgViewFiles)
198.
198
{
199.
199
int
width =
0
;
200.
200
//使列自使用宽度
201.
201
//对于DataGridView的每一个列都调整
202.
202
for
(
int
i =
0
; i < dgViewFiles.Columns.Count; i++)
203.
203
{
204.
204
//将每一列都调整为自动适应模式
205.
205
dgViewFiles.AutoResizeColumn(i, DataGridViewAutoSizeColumnMode.AllCells);
206.
206
//记录整个DataGridView的宽度
207.
207
width += dgViewFiles.Columns[i].Width;
208.
208
}
209.
209
//判断调整后的宽度与原来设定的宽度的关系,如果是调整后的宽度大于原来设定的宽度,
210.
210
//则将DataGridView的列自动调整模式设置为显示的列即可,
211.
211
//如果是小于原来设定的宽度,将模式改为填充。
212.
212
if
(width > dgViewFiles.Size.Width)
213.
213
{
214.
214
dgViewFiles.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells;
215.
215
}
216.
216
else
217.
217
{
218.
218
dgViewFiles.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
219.
219
}
220.
220
//冻结某列 从左开始 0,1,2
221.
221
dgViewFiles.Columns[
1
].Frozen =
true
;
222.
222
}
223.
223
224.
224
private
void
MainForm_FormClosing(object sender, FormClosingEventArgs e)
225.
225
{
226.
226
//窗体关闭是停止线程
227.
227
// thread.Abort();
228.
228
}
229.
229
}
230.
230
}
结果
总结
第一种方式虽然一个属性可以解决跨线程的问题,但是并不完美,造成DataGridView滚动条无法使用。第二种是最常见的解决线程间操作的解决办法。第三种方式如果直接返回List<FileMessage> ,则界面仍然会有假死,无法移动的现象,应该是await之后并没有创建新的线程造成的,可以通过下面代码方式解决,如果数据量非常大,仍然会瞬间有卡顿的现象(只是看了一篇文章,出于好奇把这种方式列出来了,也算是提供一个跨线程操作UI控件的一个思路吧,不过从代码量看其实实现变的更简单了)。
1.
1
//创建Task,创建一个新的线程,不然还会出现UI假死的现象
2.
2
return
await Task.Run(() => {
return
lstFiles; });
具体细节可参考:
http://www.cnblogs.com/jesse2013/p/async-and-await.html
代码:链接:http://pan.baidu.com/s/1pJK5RJl 密码:avx1