目錄
概述
最近在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 }
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 }
文件信息類:
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 }
窗體代碼:
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 }
如果上面的代碼會報錯:
出現這個錯誤,是因為新開的線程操作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 }
關於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 }
結果

總結
第一種方式雖然一個屬性可以解決跨線程的問題,但是並不完美,造成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
