C#中子线程操作主线程中窗体上控件的方法

来源:互联网 发布:淘宝客服兼职多少钱 编辑:程序博客网 时间:2024/05/01 18:01

在C#中,直接在子线程中对窗体上的控件操作是会出现异常,这是由于子线程和运行窗体的线程是不同的空间,因此想要在子线程来操作窗体上的控件,是不可能简单的通过控件对象名来操作,但不是说不能进行操作,微软提供了Invoke的方法,其作用就是让子线程告诉窗体线程来完成相应的控件操作。

要实现该功能,有两种方法可以选择:

 

1、在程序初始化的时候对要操作的控件设置下面的属性:

System.Windows.Forms.Control.CheckForIllegalCrossThreadCalls  =  false;

这样,系统就不会再抛出上面所说的这个错误了。

从实质上说,该方法是通过采用取消线程安全保护模式的方式实现的,所以不建议采用。

 

2、基本思路如下:

把想对另一线程中的控件实施的操作放到一个函数中,然后使用delegate代理那个函数,并且在那个函数中加入一个判断,用InvokeRequired 来判断调用这个函数的线程是否和控件线程处于同一线程中,如果是则直接执行对控件的操作,否则利用该控件的Invoke或BeginInvoke方法来执行这个代理。示例代码如下:

复制代码
  1 using System;  2 using System.Collections.Generic;  3 using System.Windows.Forms;  4   5 using System.Threading;  6   7 namespace 子线程操作主线程窗体上的控件  8 {  9     public partial class frmMain : Form 10     { 11         /***************************************************** 定义该类的私有成员 ****************************************************/ 12          13         /// <summary> 14         /// 定义一个队列,用于记录用户创建的线程 15         /// 以便在窗体关闭的时候关闭所有用于创建的线程 16         /// </summary> 17         private List<Thread> ChaosThreadList; 18  19         /***************************************************** 该类的初始化相关函数 ****************************************************/ 20                  21         /// <summary> 22         /// 窗体的初始化函数,初始化线程队列ChaosThreadList 23         /// </summary> 24         public frmMain() 25         { 26             InitializeComponent(); 27             ChaosThreadList = new List<Thread>(); 28         } 29                 30         /// <summary> 31         /// 窗体的关闭事件处理函数,在该事件中将之前创建的线程全部终止 32         /// </summary> 33         /// <param name="sender"></param> 34         /// <param name="e"></param> 35         private void frmMain_FormClosed(object sender, FormClosedEventArgs e) 36         { 37             if (ChaosThreadList.Count > 0) 38             { 39                 //编列自定义队列,将所有线程终止 40                 foreach (Thread tWorkingThread in ChaosThreadList) 41                 { 42                     tWorkingThread.Abort(); 43                 } 44             } 45         }         46  47         /***************************************************** 定义该类的自定义函数 ****************************************************/ 48  49         /// <summary> 50         /// 定义一个代理 51         /// </summary> 52         /// <param name="index"></param> 53         /// <param name="MSG"></param> 54         private delegate void DispMSGDelegate(int index,string MSG); 55  56         /// <summary> 57         /// 定义一个函数,用于向窗体上的ListView控件添加内容 58         /// </summary> 59         /// <param name="iIndex"></param> 60         /// <param name="strMsg"></param> 61         private void DispMsg(int iIndex,string strMsg) 62         { 63             if (this.lstMain.InvokeRequired==false)                      //如果调用该函数的线程和控件lstMain位于同一个线程内 64             { 65                 //直接将内容添加到窗体的控件上 66                 ListViewItem lvi = new ListViewItem(); 67                 lvi.SubItems[0].Text = iIndex.ToString(); 68                 lvi.SubItems.Add(strMsg); 69                 this.lstMain.Items.Insert(0, lvi); 70             } 71             else                                                        //如果调用该函数的线程和控件lstMain不在同一个线程 72             { 73                 //通过使用Invoke的方法,让子线程告诉窗体线程来完成相应的控件操作 74                 DispMSGDelegate DMSGD = new DispMSGDelegate(DispMsg); 75  76                 //使用控件lstMain的Invoke方法执行DMSGD代理(其类型是DispMSGDelegate) 77                 this.lstMain.Invoke(DMSGD, iIndex, strMsg); 78                  79             } 80         } 81  82         /// <summary> 83         /// 定义一个线程函数,用于循环向列表中添加数据 84         /// </summary> 85         private void Thread_DisplayMSG() 86         { 87             for (int i = 0; i < 10000; i++) 88             { 89                 DispMsg(i + 1, "Welcome you : " + (i + 1).ToString()); 90                 Thread.Sleep(10); 91             } 92         } 93  94         /***************************************************** 定义该类的事件处理函数 ****************************************************/ 95  96         /// <summary> 97         /// 【开始】按钮的单击事件处理函数,新建一个线程向窗体上的ListView控件填写内容 98         /// </summary> 99         /// <param name="sender"></param>100         /// <param name="e"></param>101         private void btnBegin_Click(object sender, EventArgs e)102         {103             //创建一个新的线程104             Thread tWorkingThread = new Thread(Thread_DisplayMSG);105 106             //将新建的线程加入到自定义线程队列中,以便在窗体结束时关闭所有的线程107             ChaosThreadList.Add(tWorkingThread);108 109             //开启线程110             tWorkingThread.Start();111         }     112 113     }114 }
复制代码

 

 

这样子就可以实现用子线程去操作主线程窗体上的控件的内容,同时,又不影响主线程对窗体上其他控件的响应。程序运行截图如下:

 

 

 

 点击[开始]按钮,程序开启一个新的线程,不断向列表中添加新的数据,而同时不会影响主界面对其它控件(例如:文本框)的响应。

0 0
原创粉丝点击