首页 > 代码库 > [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 }
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 }
文件信息类:
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 }
窗体代码:
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 }
如果上面的代码会报错:
出现这个错误,是因为新开的线程操作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 }
关于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 }
结果
总结
第一种方式虽然一个属性可以解决跨线程的问题,但是并不完美,造成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