Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 734|回复: 0

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

[复制链接]
  • TA的每日心情
    奋斗
    2024-4-6 11:05
  • 签到天数: 748 天

    [LV.9]以坛为家II

    2034

    主题

    2092

    帖子

    70万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    705612
    发表于 2021-4-16 12:14:05 | 显示全部楼层 |阅读模式

    目录

    概述

    取消跨线程检查

    使用委托异步调用

    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);
    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);
    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.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);
    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);
    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.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);
    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);
    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.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

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2024-5-18 17:11 , Processed in 0.078234 second(s), 30 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表