目录
概述
取消跨线程检查
使用委托异步调用
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?