• 在C#中子线程如何操作主窗口线程上的控件


                                                           在C#中子线程怎样操作主线程中窗口上控件


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

            要实现该功能,基本思路例如以下:

            把想对还有一线程中的控件实施的操作放到一个函数中,然后使用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         /// 【開始】button的单击事件处理函数,新建一个线程向窗口上的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 } 
    

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

      

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

     

    [P.S]:

    INVOKE方法的作用:

    它允许在那里运行的线程控制Invoke在代理指定的方法参数。这是我们要控制的操作执行的主线程运行。


  • 相关阅读:
    万万没想到,JVM内存结构的面试题可以问的这么难?
    理解JVM运行时数据区域,看这一篇文章就够了
    JVM扫盲:虚拟机内存模型与高效并发
    Java虚拟机难?一文了解JVM
    一篇简单易懂的原理文章,让你把JVM玩弄与手掌之中
    简单理解:JVM为什么需要GC
    一文让你读懂Java类加载机制!
    JVM结构的简单梳理
    深入理解JVM的类加载
    BAT面试必问题系列:JVM的判断对象是否已死和四种垃圾回收算法总结
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/4600749.html
Copyright © 2020-2023  润新知