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

来源:互联网 发布:河池网络问政平台 编辑:程序博客网 时间:2024/06/08 01:03

目录

概述

取消跨线程检查

使用委托异步调用

sync和await

总结

概述

最近在qq群里有一朋友,问起在winform中怎么通过开启线程的方式去处理耗时的操作,比如,查看某个目录下所有的文件,或者符合要求的文件。下班回来,也研究了一下。发现多线程这块有点薄弱,也算是补一补吧。

在winform开发,经常会遇到需要在控件上加载大量数据(也就是常说的耗时操作),这会导致程序出现假死状态,这个时候我们就会想到线程。

在智能客户端应用程序中,这样的线程创建并管理用户界面 (UI),因而称为 UI 线程。

可以将 UI 线程用于所有的处理,其中包括 Web 服务调用、远程对象调用和数据库调用。然而,以这种方式使用 UI 线程通常并不是一个好主意。在大多数情况下,您不能预测调用Web 服务、远程对象或数据库会持续多久,而且在 UI 线程等待响应时,可能会导致 UI 冻结。

通过创建附加线程,应用程序可以在不使用 UI 线程的情况下执行额外的处理。当应用程序调用 Web 服务时,可以使用多线程来防止 UI 冻结或并行执行某些本地任务,以整体提高应用程序的效率。在大多数情况下,您应该坚持在单独的线程上执行任何与 UI 无关的任务。

取消跨线程检查

 案例:现在做一个这样的测试项目,我们选择一个目录通过递归的方式,遍历所有的文件,将文件信息,加载到窗体的DataGridView控件上。界面如图所示:

代码

事件参数和委托:


加载中...
view sourceprint?
01.1 using System;
02.2 using System.Collections.Generic;
03.3 using System.Linq;
04.4 using System.Text;
05.5 using System.Threading.Tasks;
06.6
07.7 namespace Wofy.ThreadDemo
08.8 {
09.9
10.10     /// <summary>
11.11     ///功能描述    :    事件参数
12.12     ///开发者      :    wolfy
13.13     ///建立时间    :    2014年07月19日
14.14     ///修订描述    :   
15.15     ///进度描述    :   
16.16     ///版本号      :    1.0
17.17     ///最后修改时间:    2014年07月19日
18.18     /// </summary>
19.19     public class FileMessageEventArgs:EventArgs
20.20     {
21.21         public FileMessage fileMessage{set;get;}
22.22     }
23.23 }
FileMessageEventArgs.cs 加载中...
view sourceprint?
01.1 using System;
02.2 using System.Collections.Generic;
03.3 using System.Linq;
04.4 using System.Text;
05.5 using System.Threading.Tasks;
06.6
07.7 namespace Wofy.ThreadDemo
08.8 {
09.9
10.10     /// <summary>
11.11     ///功能描述    :    文件信息委托
12.12     ///开发者      :    wolfy
13.13     ///建立时间    :    2014年07月19日
14.14     ///修订描述    :   
15.15     ///进度描述    :   
16.16     ///版本号      :    1.0
17.17     ///最后修改时间:    2014年07月19日
18.18     /// </summary>
19.19     public delegate void FileMessageEventHandler(object sender, FileMessageEventArgs e);
20.20
21.21 }
FileMessageEventHandler.cs

文件信息类:


加载中...
view sourceprint?
01.1 using System;
02.2 using System.Collections.Generic;
03.3 using System.ComponentModel;
04.4 using System.Linq;
05.5 using System.Text;
06.6 using System.Threading.Tasks;
07.7
08.8 namespace Wofy.ThreadDemo
09.9 {
10.10     /// <summary>
11.11     /// 文件信息
12.12     /// </summary>
13.13     public class FileMessage
14.14     {
15.15         /// <summary>
16.16         /// 序号
17.17         /// </summary>
18.18         [Description('序号')]
19.19         public int intCount { get; set; }
20.20         /// <summary>
21.21         /// 文件路径
22.22         /// </summary>
23.23         [Description('文件路径')]
24.24         public string strFilePath { set; get; }
25.25         /// <summary>
26.26         /// 文件名
27.27         /// </summary>
28.28         [Description('文件名')]
29.29         public string strFileName { set; get; }
30.30         /// <summary>
31.31         /// 文件类型
32.32         /// </summary>
33.33         [Description('文件类型')]
34.34         public string strFileType { set; get; }
35.35     }
36.36 }
FileMessage.cs

窗体代码:


加载中...
view sourceprint?
001.1 using System;
002.2 using System.Collections.Generic;
003.3 using System.ComponentModel;
004.4 using System.Data;
005.5 using System.Drawing;
006.6 using System.Linq;
007.7 using System.Reflection;
008.8 using System.Text;
009.9 using System.Threading.Tasks;
010.10 using System.Windows.Forms;
011.11 using System.IO;
012.12 using System.Threading;
013.13 namespace Wofy.ThreadDemo
014.14 {
015.15     /// <summary>
016.16     ///功能描述    :    文件<a href="http://www.it165.net/edu/ewl/" target="_blank" class="keylink">浏览器</a>主窗口
017.17     ///开发者      :    wolfy
018.18     ///建立时间    :    2014年07月19日
019.19     ///修订描述    :   
020.20     ///进度描述    :   
021.21     ///版本号      :    1.0
022.22     ///最后修改时间:    2014年07月19日
023.23     /// </summary>
024.24     public partial class MainForm : Form
025.25     {
026.26         public MainForm()
027.27         {
028.28             InitializeComponent();
029.29             //取消跨线程检查
030.30            // Form.CheckForIllegalCrossThreadCalls = false;
031.31         }
032.32         private event FileMessageEventHandler fileMessageEventHandler;
033.33         private void btnSelectPath_Click(object sender, EventArgs e)
034.34         {
035.35             FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog();
036.36             if (folderBrowserDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
037.37             {
038.38                 //目录路径
039.39                 this.txtPath.Text = folderBrowserDialog.SelectedPath;
040.40                 fileMessageEventHandler += MainForm_fileMessageEventHandler;
041.41                 Thread thread = new Thread(new ParameterizedThreadStart(GetFiles));
042.42                 thread.IsBackground = true;
043.43                 thread.Start(this.txtPath.Text);
044.44             }
045.45
046.46         }
047.47         /// <summary>
048.48         /// 文件信息事件处理
049.49         /// </summary>
050.50         /// <param name='sender'></param>
051.51         /// <param name='e'></param>
052.52         void MainForm_fileMessageEventHandler(object sender, FileMessageEventArgs e)
053.53         {
054.54             FileMessage fileMessage = e.fileMessage;
055.55             this.dgViewFiles.Rows.Add(new object[] { fileMessage.intCount, fileMessage.strFilePath, fileMessage.strFileName, fileMessage.strFileType });
056.56         }
057.57     
058.58         private List<string> lstTypes = null;
059.59         static object _objLock = new object();
060.60         int intFileCount = 1;
061.61         /// <summary>
062.62         /// 递归获得文件信息
063.63         /// </summary>
064.64         /// <param name='strPath'></param>
065.65         /// <returns></returns>
066.66         public void GetFiles(object obj)
067.67         {
068.68             string strPath = obj.ToString();
069.69             List<FileMessage> lstFiles = new List<FileMessage>();
070.70
071.71             //单例创建集合
072.72             if (lstTypes == null)
073.73             {
074.74                 lock (_objLock)
075.75                 {
076.76                     if (lstTypes == null)
077.77                     {
078.78                         lstTypes = GetCheckedFileType();
079.79                     }
080.80                 }
081.81             }
082.82             string[] files = new string[0];
083.83             if (lstTypes.Count > 0)
084.84             {
085.85                 foreach (string strType in lstTypes)
086.86                 {
087.87                     files = Directory.GetFiles(strPath, '*' + strType);
088.88                     AddFileMessage(files);
089.89                 }
090.90             }
091.91             else
092.92             {
093.93                 files = Directory.GetFiles(strPath);
094.94                 AddFileMessage(files);
095.95             }
096.96             string[] strDirs = Directory.GetDirectories(strPath);
097.97             for (int i = 0; i < strDirs.Length; i++)
098.98             {
099.99                 GetFiles(strDirs[i]);
100.100             }
101.101         }
102.102         /// <summary>
103.103         /// 将信息添加到集合
104.104         /// </summary>
105.105         /// <param name='files'></param>
106.106         private void AddFileMessage(string[] files)
107.107         {
108.108             for (int i = 0; i < files.Length; i++)
109.109             {
110.110                 FileInfo fileInfo = new FileInfo(files[i]);
111.111                 FileMessage fileMessage = new FileMessage();
112.112                 fileMessage.intCount = intFileCount++;
113.113                 fileMessage.strFileName = Path.GetFileName(fileInfo.FullName);
114.114                 fileMessage.strFilePath = fileInfo.FullName;
115.115                 fileMessage.strFileType = fileInfo.Extension;
116.116                 FileMessageEventArgs e = new FileMessageEventArgs();
117.117                 e.fileMessage = fileMessage;
118.118                 this.fileMessageEventHandler(null, e);
119.119             }
120.120         }
121.121         /// <summary>
122.122         /// 获得选择的文件类型
123.123         /// </summary>
124.124         /// <returns></returns>
125.125         private List<string> GetCheckedFileType()
126.126         {
127.127             List<string> lstFileTypes = new List<string>();
128.128             foreach (Control control in this.Controls)
129.129             {
130.130                 if (control is CheckBox)
131.131                 {
132.132                     CheckBox checkBox = control as CheckBox;
133.133                     if (checkBox != null && checkBox.Checked)
134.134                     {
135.135                         lstFileTypes.Add(checkBox.Text);
136.136                     }
137.137                 }
138.138             }
139.139             return lstFileTypes;
140.140         }
141.141         /// <summary>
142.142         /// 窗体加载
143.143         /// </summary>
144.144         /// <param name='sender'></param>
145.145         /// <param name='e'></param>
146.146         private void MainForm_Load(object sender, EventArgs e)
147.147         {
148.148             //通过反射的方式添加列
149.149             Type type = typeof(FileMessage);
150.150             PropertyInfo[] propertyInfos = type.GetProperties();
151.151             foreach (PropertyInfo propertyInfo in propertyInfos)
152.152             {
153.153                 object[] objs = propertyInfo.GetCustomAttributes(typeof(DescriptionAttribute), true);
154.154                 if (objs.Length > 0)
155.155                 {
156.156                     DescriptionAttribute attr = objs[0] as DescriptionAttribute;
157.157                     string result = attr.Description;
158.158                     this.dgViewFiles.Columns.Add(result, result);
159.159                 }
160.160             }
161.161             //调整列宽
162.162             AutoSizeColumn(dgViewFiles);
163.163
164.164
165.165         }
166.166         /// <summary>
167.167         /// 使DataGridView的列自适应宽度
168.168         /// </summary>
169.169         /// <param name='dgViewFiles'></param>
170.170         private void AutoSizeColumn(DataGridView dgViewFiles)
171.171         {
172.172             int width = 0;
173.173             //使列自使用宽度
174.174             //对于DataGridView的每一个列都调整
175.175             for (int i = 0; i < dgViewFiles.Columns.Count; i++)
176.176             {
177.177                 //将每一列都调整为自动适应模式
178.178                 dgViewFiles.AutoResizeColumn(i, DataGridViewAutoSizeColumnMode.AllCells);
179.179                 //记录整个DataGridView的宽度
180.180                 width += dgViewFiles.Columns[i].Width;
181.181             }
182.182             //判断调整后的宽度与原来设定的宽度的关系,如果是调整后的宽度大于原来设定的宽度,
183.183             //则将DataGridView的列自动调整模式设置为显示的列即可,
184.184             //如果是小于原来设定的宽度,将模式改为填充。
185.185             if (width > dgViewFiles.Size.Width)
186.186             {
187.187                 dgViewFiles.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells;
188.188             }
189.189             else
190.190             {
191.191                 dgViewFiles.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
192.192             }
193.193             //冻结某列 从左开始 0,1,2
194.194             dgViewFiles.Columns[1].Frozen = true;
195.195         }
196.196     }
197.197 }
MainForm.cs

如果上面的代码会报错:

出现这个错误,是因为新开的线程操作UI主线程上的控件导致的。也就有了第一种解决方案,添加如下代码即可解决问题:

view sourceprint?
1.1  //取消跨线程检查
2.2  Control.CheckForIllegalCrossThreadCalls = false;

取消跨线程检测,总感觉心里不爽,它们是线程安全的,这里非得强制去取消,总感觉有什么隐患似的。虽然解决了问题,但是对DataGridView滚动条却无法使用了。这里就有了常规使用的第二种方案,通过委托来实现。

使用委托异步调用

使用委托修改原来的代码:


加载中...
view sourceprint?
001.1 using System;
002.2 using System.Collections.Generic;
003.3 using System.ComponentModel;
004.4 using System.Data;
005.5 using System.Drawing;
006.6 using System.Linq;
007.7 using System.Reflection;
008.8 using System.Text;
009.9 using System.Threading.Tasks;
010.10 using System.Windows.Forms;
011.11 using System.IO;
012.12 using System.Threading;
013.13 namespace Wofy.ThreadDemo
014.14 {
015.15     /// <summary>
016.16     ///功能描述    :    文件<a href="http://www.it165.net/edu/ewl/" target="_blank" class="keylink">浏览器</a>主窗口
017.17     ///开发者      :    wolfy
018.18     ///建立时间    :    2014年07月19日
019.19     ///修订描述    :   
020.20     ///进度描述    :   
021.21     ///版本号      :    1.0
022.22     ///最后修改时间:    2014年07月19日
023.23     /// </summary>
024.24     public partial class MainForm : Form
025.25     {
026.26         public MainForm()
027.27         {
028.28             InitializeComponent();
029.29         }
030.30         private event FileMessageEventHandler fileMessageEventHandler;
031.31         Thread thread;
032.32         private void btnSelectPath_Click(object sender, EventArgs e)
033.33         {
034.34             FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog();
035.35             if (folderBrowserDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
036.36             {
037.37                 //目录路径
038.38                 this.txtPath.Text = folderBrowserDialog.SelectedPath;
039.39                 fileMessageEventHandler += MainForm_fileMessageEventHandler;
040.40                 thread = new Thread(new ParameterizedThreadStart(GetFiles));
041.41                 thread.IsBackground = true;
042.42                 thread.Start(this.txtPath.Text);
043.43             }
044.44
045.45         }
046.46         //委托
047.47         private delegate void DelegateSetDataGridView(FileMessage fileMessage);
048.48         /// <summary>
049.49         ///
050.50         /// </summary>
051.51         /// <param name='fileMessage'></param>
052.52         private void SetDataGridView(FileMessage fileMessage)
053.53         {
054.54             //获取一个值,该值指示调用方在对控件进行方法调用时是否必须调用 Invoke 方法,因为调用方位于创建控件所在的线程以外的线程中。
055.55             if (this.dgViewFiles.InvokeRequired)
056.56             {
057.57                 Invoke(new DelegateSetDataGridView(SetDataGridView), new object[] { fileMessage });
058.58             }
059.59             else
060.60             {
061.61                 this.dgViewFiles.Rows.Add(new object[] { fileMessage.intCount, fileMessage.strFilePath, fileMessage.strFileName, fileMessage.strFileType });
062.62             }
063.63
064.64         }
065.65       
066.66         /// <summary>
067.67         /// 文件信息事件处理
068.68         /// </summary>
069.69         /// <param name='sender'></param>
070.70         /// <param name='e'></param>
071.71         void MainForm_fileMessageEventHandler(object sender, FileMessageEventArgs e)
072.72         {
073.73             FileMessage fileMessage = e.fileMessage;
074.74             SetDataGridView(fileMessage);
075.75         }
076.76
077.77         private List<string> lstTypes = null;
078.78         static object _objLock = new object();
079.79         int intFileCount = 1;
080.80         /// <summary>
081.81         /// 递归获得文件信息
082.82         /// </summary>
083.83         /// <param name='strPath'></param>
084.84         /// <returns></returns>
085.85         public void GetFiles(object obj)
086.86         {
087.87             string strPath = obj.ToString();
088.88             List<FileMessage> lstFiles = new List<FileMessage>();
089.89
090.90             //单例创建集合
091.91             if (lstTypes == null)
092.92             {
093.93                 lock (_objLock)
094.94                 {
095.95                     if (lstTypes == null)
096.96                     {
097.97                         lstTypes = GetCheckedFileType();
098.98                     }
099.99                 }
100.100             }
101.101             string[] files = new string[0];
102.102             if (lstTypes.Count > 0)
103.103             {
104.104                 foreach (string strType in lstTypes)
105.105                 {
106.106                     files = Directory.GetFiles(strPath, '*' + strType);
107.107                     AddFileMessage(files);
108.108                 }
109.109             }
110.110             else
111.111             {
112.112                 files = Directory.GetFiles(strPath);
113.113                 AddFileMessage(files);
114.114             }
115.115             string[] strDirs = Directory.GetDirectories(strPath);
116.116             for (int i = 0; i < strDirs.Length; i++)
117.117             {
118.118                 GetFiles(strDirs[i]);
119.119             }
120.120         }
121.121         /// <summary>
122.122         /// 将信息添加到集合
123.123         /// </summary>
124.124         /// <param name='files'></param>
125.125         private void AddFileMessage(string[] files)
126.126         {
127.127             for (int i = 0; i < files.Length; i++)
128.128             {
129.129                 FileInfo fileInfo = new FileInfo(files[i]);
130.130                 FileMessage fileMessage = new FileMessage();
131.131                 fileMessage.intCount = intFileCount++;
132.132                 fileMessage.strFileName = Path.GetFileName(fileInfo.FullName);
133.133                 fileMessage.strFilePath = fileInfo.FullName;
134.134                 fileMessage.strFileType = fileInfo.Extension;
135.135                 FileMessageEventArgs e = new FileMessageEventArgs();
136.136                 e.fileMessage = fileMessage;
137.137                 this.fileMessageEventHandler(null, e);
138.138             }
139.139         }
140.140         /// <summary>
141.141         /// 获得选择的文件类型
142.142         /// </summary>
143.143         /// <returns></returns>
144.144         private List<string> GetCheckedFileType()
145.145         {
146.146             List<string> lstFileTypes = new List<string>();
147.147             foreach (Control control in this.Controls)
148.148             {
149.149                 if (control is CheckBox)
150.150                 {
151.151                     CheckBox checkBox = control as CheckBox;
152.152                     if (checkBox != null && checkBox.Checked)
153.153                     {
154.154                         lstFileTypes.Add(checkBox.Text);
155.155                     }
156.156                 }
157.157             }
158.158             return lstFileTypes;
159.159         }
160.160         /// <summary>
161.161         /// 窗体加载
162.162         /// </summary>
163.163         /// <param name='sender'></param>
164.164         /// <param name='e'></param>
165.165         private void MainForm_Load(object sender, EventArgs e)
166.166         {
167.167             //通过反射的方式添加列
168.168             Type type = typeof(FileMessage);
169.169             PropertyInfo[] propertyInfos = type.GetProperties();
170.170             foreach (PropertyInfo propertyInfo in propertyInfos)
171.171             {
172.172                 object[] objs = propertyInfo.GetCustomAttributes(typeof(DescriptionAttribute), true);
173.173                 if (objs.Length > 0)
174.174                 {
175.175                     DescriptionAttribute attr = objs[0] as DescriptionAttribute;
176.176                     string result = attr.Description;
177.177                     this.dgViewFiles.Columns.Add(result, result);
178.178                 }
179.179             }
180.180             //调整列宽
181.181             AutoSizeColumn(dgViewFiles);
182.182
183.183
184.184         }
185.185         /// <summary>
186.186         /// 使DataGridView的列自适应宽度
187.187         /// </summary>
188.188         /// <param name='dgViewFiles'></param>
189.189         private void AutoSizeColumn(DataGridView dgViewFiles)
190.190         {
191.191             int width = 0;
192.192             //使列自使用宽度
193.193             //对于DataGridView的每一个列都调整
194.194             for (int i = 0; i < dgViewFiles.Columns.Count; i++)
195.195             {
196.196                 //将每一列都调整为自动适应模式
197.197                 dgViewFiles.AutoResizeColumn(i, DataGridViewAutoSizeColumnMode.AllCells);
198.198                 //记录整个DataGridView的宽度
199.199                 width += dgViewFiles.Columns[i].Width;
200.200             }
201.201             //判断调整后的宽度与原来设定的宽度的关系,如果是调整后的宽度大于原来设定的宽度,
202.202             //则将DataGridView的列自动调整模式设置为显示的列即可,
203.203             //如果是小于原来设定的宽度,将模式改为填充。
204.204             if (width > dgViewFiles.Size.Width)
205.205             {
206.206                 dgViewFiles.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells;
207.207             }
208.208             else
209.209             {
210.210                 dgViewFiles.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
211.211             }
212.212             //冻结某列 从左开始 0,1,2
213.213             dgViewFiles.Columns[1].Frozen = true;
214.214         }
215.215
216.216         private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
217.217         {
218.218             //窗体关闭是停止线程
219.219             thread.Abort();
220.220         }
221.221     }
222.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改写上面的代码:


加载中...
view sourceprint?
001.1 using System;
002.2 using System.Collections.Generic;
003.3 using System.ComponentModel;
004.4 using System.Data;
005.5 using System.Drawing;
006.6 using System.Linq;
007.7 using System.Reflection;
008.8 using System.Text;
009.9 using System.Threading.Tasks;
010.10 using System.Windows.Forms;
011.11 using System.IO;
012.12 using System.Threading;
013.13 namespace Wofy.ThreadDemo
014.14 {
015.15     /// <summary>
016.16     ///功能描述    :    文件浏览器主窗口
017.17     ///开发者      :    wolfy
018.18     ///建立时间    :    2014年07月19日
019.19     ///修订描述    :   
020.20     ///进度描述    :   
021.21     ///版本号      :    1.0
022.22     ///最后修改时间:    2014年07月19日
023.23     /// </summary>
024.24     public partial class MainForm : Form
025.25     {
026.26         public MainForm()
027.27         {
028.28             InitializeComponent();
029.29         }
030.30         private event FileMessageEventHandler fileMessageEventHandler;
031.31         //Thread thread;
032.32         //注意加上async
033.33         private async void btnSelectPath_Click(object sender, EventArgs e)
034.34         {
035.35             FolderBrowserDialog folderBrowserDialog = new FolderBrowserDialog();
036.36             if (folderBrowserDialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
037.37             {
038.38                 //目录路径
039.39                 this.txtPath.Text = folderBrowserDialog.SelectedPath;
040.40                 fileMessageEventHandler += MainForm_fileMessageEventHandler;
041.41                 await GetFiles(this.txtPath.Text);
042.42
043.43                 //thread = new Thread(new ParameterizedThreadStart(GetFiles));
044.44                 //thread.IsBackground = true;
045.45                 //thread.Start(this.txtPath.Text);
046.46
047.47             }
048.48
049.49         }
050.50         //委托
051.51         private delegate void DelegateSetDataGridView(FileMessage fileMessage);
052.52         /// <summary>
053.53         ///
054.54         /// </summary>
055.55         /// <param name='fileMessage'></param>
056.56         private void SetDataGridView(FileMessage fileMessage)
057.57         {
058.58             //获取一个值,该值指示调用方在对控件进行方法调用时是否必须调用 Invoke 方法,因为调用方位于创建控件所在的线程以外的线程中。
059.59             if (this.dgViewFiles.InvokeRequired)
060.60             {
061.61                 Invoke(new DelegateSetDataGridView(SetDataGridView), new object[] { fileMessage });
062.62             }
063.63             else
064.64             {
065.65                 this.dgViewFiles.Rows.Add(new object[] { fileMessage.intCount, fileMessage.strFilePath, fileMessage.strFileName, fileMessage.strFileType });
066.66             }
067.67
068.68         }
069.69
070.70         /// <summary>
071.71         /// 文件信息事件处理
072.72         /// </summary>
073.73         /// <param name='sender'></param>
074.74         /// <param name='e'></param>
075.75         void MainForm_fileMessageEventHandler(object sender, FileMessageEventArgs e)
076.76         {
077.77             FileMessage fileMessage = e.fileMessage;
078.78             // SetDataGridView(fileMessage);
079.79             this.dgViewFiles.Rows.Add(new object[] { fileMessage.intCount, fileMessage.strFilePath, fileMessage.strFileName, fileMessage.strFileType });
080.80         }
081.81
082.82         private List<string> lstTypes = null;
083.83         static object _objLock = new object();
084.84         int intFileCount = 1;
085.85         /// <summary>
086.86         /// 递归获得文件信息
087.87         /// </summary>
088.88         /// <param name='strPath'></param>
089.89         /// <returns></returns>
090.90         public async Task<List<FileMessage>> GetFiles(object obj)
091.91         {
092.92             string strPath = obj.ToString();
093.93             List<FileMessage> lstFiles = new List<FileMessage>();
094.94
095.95             //单例创建集合
096.96             if (lstTypes == null)
097.97             {
098.98                 lock (_objLock)
099.99                 {
100.100                     if (lstTypes == null)
101.101                     {
102.102                         lstTypes = GetCheckedFileType();
103.103                     }
104.104                 }
105.105             }
106.106             string[] files = new string[0];
107.107             if (lstTypes.Count > 0)
108.108             {
109.109                 foreach (string strType in lstTypes)
110.110                 {
111.111                     files = Directory.GetFiles(strPath, '*' + strType);
112.112                     AddFileMessage(files);
113.113                 }
114.114             }
115.115             else
116.116             {
117.117                 files = Directory.GetFiles(strPath);
118.118                 AddFileMessage(files);
119.119             }
120.120             string[] strDirs = Directory.GetDirectories(strPath);
121.121             for (int i = 0; i < strDirs.Length; i++)
122.122             {
123.123                 await GetFiles(strDirs[i]);
124.124             }
125.125             //创建Task,创建一个新的线程,不然还会出现UI假死的现象
126.126             return await Task.Run(() => { return lstFiles; });
127.127             
128.128         }
129.129         /// <summary>
130.130         /// 将信息添加到集合
131.131         /// </summary>
132.132         /// <param name='files'></param>
133.133         private void AddFileMessage(string[] files)
134.134         {
135.135             for (int i = 0; i < files.Length; i++)
136.136             {
137.137                 FileInfo fileInfo = new FileInfo(files[i]);
138.138                 FileMessage fileMessage = new FileMessage();
139.139                 fileMessage.intCount = intFileCount++;
140.140                 fileMessage.strFileName = Path.GetFileName(fileInfo.FullName);
141.141                 fileMessage.strFilePath = fileInfo.FullName;
142.142                 fileMessage.strFileType = fileInfo.Extension;
143.143                 FileMessageEventArgs e = new FileMessageEventArgs();
144.144                 e.fileMessage = fileMessage;
145.145                 this.fileMessageEventHandler(null, e);
146.146             }
147.147         }
148.148         /// <summary>
149.149         /// 获得选择的文件类型
150.150         /// </summary>
151.151         /// <returns></returns>
152.152         private List<string> GetCheckedFileType()
153.153         {
154.154             List<string> lstFileTypes = new List<string>();
155.155             foreach (Control control in this.Controls)
156.156             {
157.157                 if (control is CheckBox)
158.158                 {
159.159                     CheckBox checkBox = control as CheckBox;
160.160                     if (checkBox != null && checkBox.Checked)
161.161                     {
162.162                         lstFileTypes.Add(checkBox.Text);
163.163                     }
164.164                 }
165.165             }
166.166             return lstFileTypes;
167.167         }
168.168         /// <summary>
169.169         /// 窗体加载
170.170         /// </summary>
171.171         /// <param name='sender'></param>
172.172         /// <param name='e'></param>
173.173         private void MainForm_Load(object sender, EventArgs e)
174.174         {
175.175             //通过反射的方式添加列
176.176             Type type = typeof(FileMessage);
177.177             PropertyInfo[] propertyInfos = type.GetProperties();
178.178             foreach (PropertyInfo propertyInfo in propertyInfos)
179.179             {
180.180                 object[] objs = propertyInfo.GetCustomAttributes(typeof(DescriptionAttribute), true);
181.181                 if (objs.Length > 0)
182.182                 {
183.183                     DescriptionAttribute attr = objs[0] as DescriptionAttribute;
184.184                     string result = attr.Description;
185.185                     this.dgViewFiles.Columns.Add(result, result);
186.186                 }
187.187             }
188.188             //调整列宽
189.189             AutoSizeColumn(dgViewFiles);
190.190
191.191
192.192         }
193.193         /// <summary>
194.194         /// 使DataGridView的列自适应宽度
195.195         /// </summary>
196.196         /// <param name='dgViewFiles'></param>
197.197         private void AutoSizeColumn(DataGridView dgViewFiles)
198.198         {
199.199             int width = 0;
200.200             //使列自使用宽度
201.201             //对于DataGridView的每一个列都调整
202.202             for (int i = 0; i < dgViewFiles.Columns.Count; i++)
203.203             {
204.204                 //将每一列都调整为自动适应模式
205.205                 dgViewFiles.AutoResizeColumn(i, DataGridViewAutoSizeColumnMode.AllCells);
206.206                 //记录整个DataGridView的宽度
207.207                 width += dgViewFiles.Columns[i].Width;
208.208             }
209.209             //判断调整后的宽度与原来设定的宽度的关系,如果是调整后的宽度大于原来设定的宽度,
210.210             //则将DataGridView的列自动调整模式设置为显示的列即可,
211.211             //如果是小于原来设定的宽度,将模式改为填充。
212.212             if (width > dgViewFiles.Size.Width)
213.213             {
214.214                 dgViewFiles.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.DisplayedCells;
215.215             }
216.216             else
217.217             {
218.218                 dgViewFiles.AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.Fill;
219.219             }
220.220             //冻结某列 从左开始 0,1,2
221.221             dgViewFiles.Columns[1].Frozen = true;
222.222         }
223.223
224.224         private void MainForm_FormClosing(object sender, FormClosingEventArgs e)
225.225         {
226.226             //窗体关闭是停止线程
227.227             // thread.Abort();
228.228         }
229.229     }
230.230 }
MainForm.cs

结果

总结

第一种方式虽然一个属性可以解决跨线程的问题,但是并不完美,造成DataGridView滚动条无法使用。第二种是最常见的解决线程间操作的解决办法。第三种方式如果直接返回List<FileMessage> ,则界面仍然会有假死,无法移动的现象,应该是await之后并没有创建新的线程造成的,可以通过下面代码方式解决,如果数据量非常大,仍然会瞬间有卡顿的现象(只是看了一篇文章,出于好奇把这种方式列出来了,也算是提供一个跨线程操作UI控件的一个思路吧,不过从代码量看其实实现变的更简单了)。

view sourceprint?
1.1  //创建Task,创建一个新的线程,不然还会出现UI假死的现象
2.2  return await Task.Run(() => { return lstFiles; });

具体细节可参考:

http://www.cnblogs.com/jesse2013/p/async-and-await.html

代码:链接:http://pan.baidu.com/s/1pJK5RJl 密码:avx1

0 0
原创粉丝点击