Winform线程间操作无效从不是创建控件的线程访问它的几个解决方案async和await?

目录

概述

取消跨线程检查

使用委托异步调用

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 }
FileMessageEventArgs.cs  加载中...
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 }
FileMessageEventHandler.cs

文件信息类:


加载中...
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 }
FileMessage.cs

窗体代码:


加载中...
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 }
MainForm.cs

如果上面的代码会报错:

出现这个错误,是因为新开的线程操作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 }
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改写上面的代码:


加载中...
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 }
MainForm.cs

结果

总结

第一种方式虽然一个属性可以解决跨线程的问题,但是并不完美,造成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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值