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

[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     ///开发者      :    wolfy13     ///建立时间    :    2014年07月19日14     ///修订描述    :    15     ///进度描述    :    16     ///版本号      :    1.017     ///最后修改时间:    2014年07月19日18     /// </summary>19     public class FileMessageEventArgs:EventArgs20     {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     ///开发者      :    wolfy13     ///建立时间    :    2014年07月19日14     ///修订描述    :    15     ///进度描述    :    16     ///版本号      :    1.017     ///最后修改时间:    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 FileMessage14     {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             else190             {191                 dgViewFiles.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;192             }193             //冻结某列 从左开始 0,1,2194             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             else111             {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             else209             {210                 dgViewFiles.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;211             }212             //冻结某列 从左开始 0,1,2213             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             else116             {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             else217             {218                 dgViewFiles.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;219             }220             //冻结某列 从左开始 0,1,2221             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