|
在C#中,直接在子线程中对窗体上的控件操作是会出现异常,这是由于子线程和运行窗体的线程是不同的空间,因此想要在子线程来操作窗体上的控件,是不可能简单的通过控件对象名来操作,但不是说不能进行操作,微软提供了Invoke的方法,其作用就是让子线程告诉窗体线程来完成相应的控件操作。
要实现该功能,基本思路如下:
把想对另一线程中的控件实施的操作放到一个函数中,然后使用delegate代理那个函数,并且在那个函数中加入一个判断,用 InvokeRequired 来判断调用这个函数的线程是否和控件线程处于同一线程中,如果是则直接执行对控件的操作,否则利用该控件的Invoke或BeginInvoke方法来执行这个代理。示例代码如下:
- using System;
- using System.Collections.Generic;
- using System.Windows.Forms;
- using System.Threading;
- namespace 子线程操作主线程窗体上的控件
- {
- public partial class frmMain : Form
- {
- /********************** 定义该类的私有成员 **************************/
-
- /// <summary>
- /// 定义一个队列,用于记录用户创建的线程
- /// 以便在窗体关闭的时候关闭所有用于创建的线程
- /// </summary>
- private List<Thread> ChaosThreadList;
- /********************** 该类的初始化相关函数 ************************/
-
- /// <summary>
- /// 窗体的初始化函数,初始化线程队列ChaosThreadList
- /// </summary>
- public frmMain()
- {
- InitializeComponent();
- ChaosThreadList = new List<Thread>();
- }
-
- /// <summary>
- /// 窗体的关闭事件处理函数,在该事件中将之前创建的线程全部终止
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void frmMain_FormClosed(object sender, FormClosedEventArgs e)
- {
- if (ChaosThreadList.Count > 0)
- {
- //编列自定义队列,将所有线程终止
- foreach (Thread tWorkingThread in ChaosThreadList)
- {
- tWorkingThread.Abort();
- }
- }
- }
- /**************************** 定义该类的自定义函数 ***********************/
- /// <summary>
- /// 定义一个代理
- /// </summary>
- /// <param name="index"></param>
- /// <param name="MSG"></param>
- private delegate void DispMSGDelegate(int index,string MSG);
- /// <summary>
- /// 定义一个函数,用于向窗体上的ListView控件添加内容
- /// </summary>
- /// <param name="iIndex"></param>
- /// <param name="strMsg"></param>
- private void DispMsg(int iIndex,string strMsg)
- {
- if (this.lstMain.InvokeRequired==false) //如果调用该函数的线程和控件lstMain位于同一个线程内
- {
- //直接将内容添加到窗体的控件上
- ListViewItem lvi = new ListViewItem();
- lvi.SubItems[0].Text = iIndex.ToString();
- lvi.SubItems.Add(strMsg);
- this.lstMain.Items.Insert(0, lvi);
- }
- else //如果调用该函数的线程和控件lstMain不在同一个线程
- {
- //通过使用Invoke的方法,让子线程告诉窗体线程来完成相应的控件操作
- DispMSGDelegate DMSGD = new DispMSGDelegate(DispMsg);
- //使用控件lstMain的Invoke方法执行DMSGD代理(其类型是DispMSGDelegate)
- this.lstMain.Invoke(DMSGD, iIndex, strMsg);
-
- }
- }
- /// <summary>
- /// 定义一个线程函数,用于循环向列表中添加数据
- /// </summary>
- private void Thread_DisplayMSG()
- {
- for (int i = 0; i < 10000; i++)
- {
- DispMsg(i + 1, "Welcome you : " + (i + 1).ToString());
- Thread.Sleep(10);
- }
- }
- /******************************* 定义该类的事件处理函数 ********************************/
- /// <summary>
- /// 【开始】按钮的单击事件处理函数,新建一个线程向窗体上的ListView控件填写内容
- /// </summary>
- /// <param name="sender"></param>
- /// <param name="e"></param>
- private void btnBegin_Click(object sender, EventArgs e)
- {
- //创建一个新的线程
- Thread tWorkingThread = new Thread(Thread_DisplayMSG);
- //将新建的线程加入到自定义线程队列中,以便在窗体结束时关闭所有的线程
- ChaosThreadList.Add(tWorkingThread);
- //开启线程
- tWorkingThread.Start();
- }
- }
- }
复制代码
这样子就可以实现用子线程去操作主线程窗体上的控件的内容,同时,又不影响主线程对窗体上其他控件的响应。
点击[开始]按钮,程序开启一个新的线程,不断向列表中添加新的数据,而同时不会影响主界面对其它控件(例如:文本框)的响应。
[P.S]:
INVOKE方法的作用:
它使该控件所在的线程执行Invoke方法参数中指定的代理,也就是使主线程执行我们想对控件进行的操作。
|
|