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

目录

概述

取消跨线程检查

使用委托异步调用

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[i]);
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[i]);
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[i].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[i]);
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[i]);
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[i].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[i]);
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[i]);
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[i].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

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

时间: 2024-10-25 14:33:34

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

线程间操作无效: 从不是创建控件“”的线程访问它 解决办法(转)

线程间操作无效: 从不是创建控件“”的线程访问它 解决办法 http://blog.sina.com.cn/s/blog_568e66230101der7.html 利用FileSystemWatcher设计一个文件监控系统时,如果一个文件被修改或者新建,则文件修改事件会被多次触发而产生多条信息.为了将一个文件被修改一次而产生的多条信息归结为一条,在设计中新开了一个线程,在指定时间内(如2秒内)这个文件的修改被认为是一次修改,从而只产生一条信息. 这个工作完成后,又出现了另外一个问题:因为需要在

线程间操作无效: 从不是创建控件的线程访问它。

题目:在form窗体中有一个按钮和一个lable.点击按钮创建一个新的线程更新lable的值 1 private void button1_Click(object sender, EventArgs e) 2 { 3 //报错:从不是创建控件的线程访问它 4 Thread t = new Thread(() => 5 { 6 for (int i = 0; i < 100; i++) 7 { 8 this.label1.Text = i.ToString(); 9 } 10 }); 11 t

线程使用中常见的错误-“System.InvalidOperationException”线程间操作无效: 从不是创建控件“ ”的线程访问它。

"System.InvalidOperationException"类型的未经处理的异常在 System.Windows.Forms.dll 中发生 其他信息: 线程间操作无效: 从不是创建控件"label1"的线程访问它. 解决方法: 1 构造方法中添加 CheckForIllegalCrossThreadCalls = false; 2 方法二 invoke label1.Invoke(new Action<string>((str) => {

线程间操作无效: 从不是创建控件的线程访问它的三种方法

原文:http://www.cnblogs.com/luckboy/archive/2010/12/19/1910785.html 1.把CheckForIllegalCrossThreadCalls设置为false 2.利用委托 delegate void SetTextCallBack(string text); private void SetText(string text) { if (this.txt_a.InvokeRequired) { SetTextCallBack stcb

线程间操作无效: 从不是创建控件的线程访问它

public partial class Form1 : Form     {         private delegate void DelegateSetText(string text);         private void SetText(string text)         {             if (this.txtMessage.InvokeRequired)             {                 Invoke(new DelegateS

线程间操作无效: 从不是创建控件&ldquo;textBox2&rdquo;的线程访问它

如何:对 Windows 窗体控件进行线程安全调用 线程间操作无效: 从不是创建控件的线程访问它的三种方法 如果使用多线程处理来提高 Windows 窗体应用程序的性能,则你必须确保以线程安全的方式调用控件. 访问 Windows 窗体控件不是本身就线程安全的. 如果有两个或两个以上线程操作控件的状态,则可能迫使该控件处于不一致状态. 可能出现其他与线程相关的 bug,例如争用条件和死锁. 请务必确保以线程安全的方式访问控件. 详细资料: https://msdn.microsoft.com/z

线程间操作无效: 从不是创建控件“button1”的线程访问它。

.net2后是不能跨线程访问控件的.,窗体上的控件是当前主线程创建的,当用户异步执行一个方法:在该方法中给窗体上的控件赋值,记住:当执行一个异步委托的时候,其实 就是开了一个线程去执行那个方法,这样就会报错:线程间操作无效: 从不是创建控件“某某某”的线程访问它. C# WinForm开 发中,这是一个比较常见的异常:线程间操作无效,从不是创建控件“xxx”的线程访问它.这个异常来源于.NET2的一个限制:工作线程不能访问窗口线程 创建的控件.解决方法主要有两种,一种是在窗口线程中设置Check

C#报错&quot;线程间操作无效: 从不是创建控件“XXX”的线程访问它&quot;--解决示例

C# Winform程序中,使用线程对界面进行更新需要特殊处理,否则会出现异常“线程间操作无效: 从不是创建控件“taskView”的线程访问它.” 在网文“http://www.cnblogs.com/smileberry/p/3912918.html”的知道下,我做了下面的例程,希望对大家有所帮助,如果注释不够的话请访问原文. 例程是点击按钮后启动线程更新一个标签的文字,界面如下: 程序如下: using System; using System.Collections.Generic; u

关于“线程间操作无效: 从不是创建控件’textBox1‘的线程访问它”异常的解决方法

线程间操作无效: 从不是创建控件“textBox1”的线程访问它 背景:通过一个辅助线程计算出的一个值赋给textBox1.text;解决办法:1.直接在窗体的构造函数中加:System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls = false; 此时把所有的控件 合法性线程检查全部都给禁止掉了. 2.通过代理来解决(msdn) private delegate void SetTextCallback(string text);

线程间操作无效: 从不是创建控件“控件id”的线程访问它。(.net跨线程执行方法)

找了好久资料,终于解决了,特此记录下来. 1 delegate void DelListHandler(string number); /// <summary> /// 按标识删除listview内容 /// </summary> /// <param name="number">标识</param> private void DelListViewLog(string number) { for (int i = 0; i <