From:http://tech.ddvip.com/2008-12/122906068099474.html

一、前面的话

  对于一些耗时型操作(如文件下载),让主线程去处理不是明智的选择,虽然这样做会使得程序开发起来很简单。因为WinForm程序设计的准则之一就是Responsive,即让用户觉得程序一直在工作,而不是感觉它在罢工(呵呵,事实上,程序不会罢工,只是你没给他表现得机会,如果它有情感,会觉得委屈死)。.Net FrameWork支持在程序用应用线程编程,这可以很好的解决上述问题,不过有时候直接使用Thread和Threadstart显得有些繁琐也没必要,为此.Net Framework提供了BackgroundWorker组件来应付一些简单的应用环境。

  本文将分别对上述两种情况的跨线程操作控件方法进行阐述。

二、BackgroundWorker下的跨线程操作控件

  BackgroundWorker是个很好的伙计,因为它可以忙你搞定许多脏活累活。具体的讲,它可以自动的帮你创建工作线程,可以在工作时把工作的进展情况告诉给你,可以在工作完成时通知并帮你做一些收尾的工作,当你觉得他很烦的时候,你还可以随时让他停下来。

  BackgroundWorker组件提供了三个事件:DoWork,ProgressChanged和RunWorkerCompleted。Dowork顾名思义是用来处理工作业务的,在这里面加入你想让工作线程在后台处理的代码即可。但是在这个事件中不能加入跨线程操作的代码。如下图,当我试图改变Label.Text的值时,抛出了异常信息:

  不过这里有个例外,就是对于ToolStrip及其从该类继承过来的容器控件,某些在该容器上的控件(如StatusLabel)可以在工作线程中直接操作。至于为啥,我没有去深究,不过根据图中的提示信息,一个很合理的解释就是这类控件和BackgroundWorker由同一个线程创建。

ProgressChanged和RunWorkerCompleted事件分别用来报告工作线程的工作情况和在工作线程结束后进行一些操作。这两个事件都支持跨线程操作控件。下面通过一个简单的实例进行验证。

  用程序实现将一个目录中的文件拷贝至另外一个目录。

  1.程序界面设计如下:

  2.工作流程:(1)设置源目录和目标目录(2)拷贝文件,在ListView中显示拷贝信息,更新状态栏中的进度条和当前处理文件信息(3)拷贝过程结束后,用MessageBox提示拷贝文件数量,同时清空源目录和目标目录信息。

  3.代码实现

  1. private void bwFileCopy_DoWork(object sender, DoWorkEventArgs e)
  2.     {
  3.       DirectoryInfo di = (DirectoryInfo)e.Argument;
  4.       int iCur = 1;
  5.       foreach (FileInfo fi in di.GetFiles())
  6.       {
  7.         //为证明ToolTrip对于跨线程的特殊性,在此处更新状态栏的当前处理文件信息
  8.         //实际应用时最好放到ProgressChanged中,通过ReportProgress的参数UserState传递要处理的信息!
  9.         tsslInfo.Text = string.Format("当前正在拷贝文件:{0}", fi.Name);
  10.         fi.CopyTo(Path.Combine(targetDir,fi.Name),true);
  11.         bwFileCopy.ReportProgress(GetPercent(iCur, iFileCount),fi.Name);
  12.         iCur++;
  13.         
  14.       }
  15.       e.Result = iCur;
  16.     }
  17. private void bwFileCopy_ProgressChanged(object sender, ProgressChangedEventArgs e)
  18.     {
  19.       //在此处更新状态栏中的进度条
  20.       tssbProcess.Value = e.ProgressPercentage;
  21.       //在Listview中添加拷贝信息
  22.       string FileName = e.UserState.ToString();
  23.       lvOutput.Items.Add(new ListViewItem(new string[] {System.DateTime.Now.ToLongTimeString(),FileName})).EnsureVisible();
  24.       
  25.     }
  26. private void bwFileCopy_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
  27.     {
  28.       //清空源目录和目标目录
  29.       tbSource.Text = string.Empty;
  30.       tbTargetDir.Text = string.Empty;
  31.       //提示拷贝文件数量
  32.       MessageBox.Show(string.Format("此过程共拷贝了{0}个文件",e.Result)); 
  33.     }

三、Thread/ThreadStart下的跨线程操作控件

  在一些情况下,Thread/ThreadStart也是有一定市场的,特别在工作线程很多的情况下,显得尤为突出。事实上,在这种环境下要实现上述的例子并不难,代码也没有增加多少,前提是你必须理解Control.Invoke方法。该方法在MSDN上的解释是:在拥有此控件的基础窗口句柄的线程上执行委托。如果你注意到了第一张图片显示的异常信息,你会很快理解这个方法的重大意义。它可以让工作线程的中委托在主线程中执行!因此实现上述例子的思路就是,在工作线程中使用委托来执行操作控件的方法,然后用主窗口的Invoke方法调用!为了实现BackgroundWorker的ProgressChanged和RunWorkerCompleted事件,定义了ReportProcessInfo委托和DoneAfterCompleted委托。主要代码如下:

  1. //实现BackgroundWorker的ProgressChanged事件
  2. public delegate void ReportProcessInfo(string Info, int iPercent);
  3. /**/实现BackgroundWorker的RunWorkerCompleted事件
  4. public delegate void DoneAfterCompleted(string Info);
  5. //更新Listview和ProgressBar的方法
  6.     private void UpdateInfoToUser(string info,int percent)
  7.     {
  8.       if (InvokeRequired)
  9.         Invoke(new ReportProcessInfo(UpdateInfoToUser), info, percent);
  10.       else
  11.       {
  12.         lvOutput.Items.Add(new ListViewItem(new string[] { System.DateTime.Now.ToLongTimeString(), info })).EnsureVisible();
  13.         tssbProcess.Value = percent;
  14.       }
  15.        
  16.     }
  17.     //清空源目录和目标目录信息,显示拷贝文件数的方法
  18.     private void ShowUserFilesCountInfo(string info)
  19.     {
  20.       if (InvokeRequired)
  21.         Invoke(new DoneAfterCompleted(ShowUserFilesCountInfo), info);
  22.       else
  23.       {
  24.         tbSource.Text = string.Empty;
  25.         tbTargetDir.Text = string.Empty;
  26. MessageBox.Show(info);
  27.       }
  28.       
  29.     }
  30. //线程函数
  31. private void CopyFiles(object SourceDir)
  32.     {
  33.       DirectoryInfo di = (DirectoryInfo)SourceDir;
  34.       int icur = 0;
  35.       foreach (FileInfo fi in di.GetFiles())
  36.       {
  37.         icur++;
  38.         tsslInfo.Text = string.Format("当前正在处理文件:{0}",fi.Name);
  39.         fi.CopyTo(Path.Combine(targetDir,fi.Name),true);
  40.         CopyOneFileIsOK(fi.Name,GetPercent(icur,iFileCount));               
  41.       }
  42.       CopyFilesIsCompleted(string.Format("本次操作共拷贝了{0}个文件!",icur));
  43.     }

运行结果如下:

四、结尾

  代码中的InvokeRequired用于判断该段代码是否是在其他线程中委托调用的,如果为真,就需要在本线程中重新创建一个该委托的实例,并用Invoke方法调用它,让这段代码在本线程中调用。

  当代码中需要对多个控件进行操作,最好使用Form的InvokeRequired来判断,并使用Form的Invoke方法调用新建的委托实例。当只对某个控件操作时,就可以只用该控件的InvokeRequired和Invoke。比如tbSource,就可用tbSource.InvokeRequired和tbSource.Invoke。

C#中跨线程操作控件相关推荐

  1. c#跨线程操作控件(有UI操作)|及多线程操作

    仅记录(好多大佬都会): 跨线程操作UI控件 ※在.NET2.0之后为了线程之间的安全,不允许跨线程操作控件,最简单的解决办法是禁止检查,但一般不猜用此方法,会造成各线程之间的混乱,可用作临时调试使用 ...

  2. C#跨线程操作控件的线程安全方法

    C#跨线程操作控件的线程安全方法 在C#中,经常用到这样一个场景,Windows Form程序启动一个工作者线程执行一部分工作,这样做是为了避免速度慢的工作如果直接调用会使得主Form停止响应一段时间 ...

  3. WinForm中新开一个线程操作窗体上的控件(跨线程操作控件)GOOD

    http://www.cnblogs.com/joey0210/p/3450379.html 最近在做一个winform的小软件(抢票的...).登录窗体要从远程web页面获取一些数据,为了不阻塞登录 ...

  4. VS2005中,C#中跨线程访问控件问题解决方案

    最近我在做一个项目,遇到了跨线程要去访问页面控件.但是总是提示出错,不能在其它线程中修改创建控件的线程的控件的值,后来采用了匿名代理,结果很轻松地解决了.解决过程如下: 首先在窗体上,创建一个list ...

  5. C#中跨线程访问控件问题解决方案

    net 原则上禁止跨线程访问控件,因为这样可能造成错误的发生,推荐的解决方法是采用代理,用代理方法来间接操作不是同一线程创建的控件. 第二种方法是禁止编译器对跨线程访问作检查,可以实现访问,但是出不出 ...

  6. C# winform跨线程操作控件

    前提:当我们使用Winform开发的时候,经常会遇到:System.InvalidOperationException:"线程间操作无效: 从不是创建控件"xxxx"的线 ...

  7. 跨线程操作无效:从创建该线程的线程以外的线程访问控件

    我有一个场景. (Windows窗体,C#.. NET) 有一个主窗体可以承载一些用户控件. 用户控件执行一些繁重的数据操作,因此,如果我直接调用UserControl_Load方法,则UI在加载方法 ...

  8. 多线程总结之旅(12):跨线程调用控件的几种方式

    本来是写完线程池就结束多线程总结之旅系列的,但是想想平时在项目中用到线程仅仅不够的,为什么这么说呢?举个例子:我们有一个函数,它的功能就是加载数据,然后绑定到datagridview.现在我们开启一个 ...

  9. C# 跨线程调用控件

    在C# 的应用程序开发中, 我们经常要把UI线程和工作线程分开,防止界面停止响应.  同时我们又需要在工作线程中更新UI界面上的控件, 下面介绍几种常用的方法 阅读目录 线程间操作无效 第一种办法:禁 ...

最新文章

  1. Java中比较对象的两个接口Comparable接口和Comparator接口
  2. python 仪表盘-Python笔记:制作和自定义仪表盘
  3. C语言编译、链接过程探究
  4. php万年历月份处理_php实现万年历的完整代码
  5. [SDOI2009]Bill的挑战——全网唯一 一篇容斥题解
  6. 下拉选择框 其他_列表框 vs 下拉列表,哪个更好?
  7. arithmetic java_Java:Arithmetic
  8. Transform的normalize参数含义
  9. 分布式对象存储解决方案
  10. fterm linux ssh 乱码,用fterm ssh时出现如下乱码,请问如何解决
  11. Web版RSS阅读器(一)——dom4j读取xml(opml)文件
  12. SimpleDateFormat多线程天坑
  13. php老虎杠子鸡虫条件,老虎、杠子、鸡——在游戏中学习
  14. Linux 常用命令(后台web开发)
  15. css实现带边框,半透明气泡定位浮层效果
  16. C#用于登录数据库的方法
  17. 函数mmap()的使用
  18. Windows10家庭版升级至专业版
  19. 看板方法:向交警蜀黍学习怎么做软件
  20. WiFi共享精灵手机版隆重上线:流量“变现”WiFi

热门文章

  1. ❤️六万字《SpringMVC框架介绍—从入门到高级》(建议收藏)❤️
  2. 201512-1-数位之和
  3. Maxwell数据库数据采集-大数据week12-DAY1-Maxwell
  4. 【PowerShell】PS中 the fuck 插件(PoShFuck)将 wtf 搜索引擎从 Google 改为 Baidu 或者 Bing
  5. 【数据结构笔记43】C实现:寻找通话次数最多的电话号(散列表例题)
  6. redmin3 忘记管理密码找回方法
  7. 计算机清理的作用,电脑也有自带的垃圾清理功能,甚至比众多清理软件更好用!...
  8. 数字后端基本概念介绍<Endcap Cell>
  9. 基于Verilog实现呼吸灯
  10. html盒子优先级设置,CSS 基础(盒模型、选择器、权重、优先级)