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