四联光电智能照明论坛

 找回密码
 立即注册
搜索
热搜: 活动 交友 discuz
查看: 2243|回复: 0
打印 上一主题 下一主题

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

[复制链接]
  • TA的每日心情
    开心
    2018-11-9 08:52
  • 241

    主题

    691

    帖子

    7652

    积分

    论坛元老

    Rank: 8Rank: 8

    积分
    7652
    跳转到指定楼层
    楼主
    发表于 2016-11-5 20:16:26 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式

    在C#中,直接在子线程中对窗体上的控件操作是会出现异常,这是由于子线程和运行窗体的线程是不同的空间,因此想要在子线程来操作窗体上的控件,是不可能简单的通过控件对象名来操作,但不是说不能进行操作,微软提供了Invoke的方法,其作用就是让子线程告诉窗体线程来完成相应的控件操作。
    要实现该功能,基本思路如下:
    把想对另一线程中的控件实施的操作放到一个函数中,然后使用delegate代理那个函数,并且在那个函数中加入一个判断,用 InvokeRequired 来判断调用这个函数的线程是否和控件线程处于同一线程中,如果是则直接执行对控件的操作,否则利用该控件的Invoke或BeginInvoke方法来执行这个代理。示例代码如下:

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Windows.Forms;

    4. using System.Threading;

    5. namespace 子线程操作主线程窗体上的控件
    6. {
    7.     public partial class frmMain : Form
    8.     {
    9.         /********************** 定义该类的私有成员 **************************/
    10.         
    11.         /// <summary>
    12.         /// 定义一个队列,用于记录用户创建的线程
    13.         /// 以便在窗体关闭的时候关闭所有用于创建的线程
    14.         /// </summary>
    15.         private List<Thread> ChaosThreadList;

    16.         /********************** 该类的初始化相关函数 ************************/
    17.                
    18.         /// <summary>
    19.         /// 窗体的初始化函数,初始化线程队列ChaosThreadList
    20.         /// </summary>
    21.         public frmMain()
    22.         {
    23.             InitializeComponent();
    24.             ChaosThreadList = new List<Thread>();
    25.         }
    26.                
    27.         /// <summary>
    28.         /// 窗体的关闭事件处理函数,在该事件中将之前创建的线程全部终止
    29.         /// </summary>
    30.         /// <param name="sender"></param>
    31.         /// <param name="e"></param>
    32.         private void frmMain_FormClosed(object sender, FormClosedEventArgs e)
    33.         {
    34.             if (ChaosThreadList.Count > 0)
    35.             {
    36.                 //编列自定义队列,将所有线程终止
    37.                 foreach (Thread tWorkingThread in ChaosThreadList)
    38.                 {
    39.                     tWorkingThread.Abort();
    40.                 }
    41.             }
    42.         }        

    43.         /**************************** 定义该类的自定义函数 ***********************/

    44.         /// <summary>
    45.         /// 定义一个代理
    46.         /// </summary>
    47.         /// <param name="index"></param>
    48.         /// <param name="MSG"></param>
    49.         private delegate void DispMSGDelegate(int index,string MSG);

    50.         /// <summary>
    51.         /// 定义一个函数,用于向窗体上的ListView控件添加内容
    52.         /// </summary>
    53.         /// <param name="iIndex"></param>
    54.         /// <param name="strMsg"></param>
    55.         private void DispMsg(int iIndex,string strMsg)
    56.         {
    57.             if (this.lstMain.InvokeRequired==false)                      //如果调用该函数的线程和控件lstMain位于同一个线程内
    58.             {
    59.                 //直接将内容添加到窗体的控件上
    60.                 ListViewItem lvi = new ListViewItem();
    61.                 lvi.SubItems[0].Text = iIndex.ToString();
    62.                 lvi.SubItems.Add(strMsg);
    63.                 this.lstMain.Items.Insert(0, lvi);
    64.             }
    65.             else                                                        //如果调用该函数的线程和控件lstMain不在同一个线程
    66.             {
    67.                 //通过使用Invoke的方法,让子线程告诉窗体线程来完成相应的控件操作
    68.                 DispMSGDelegate DMSGD = new DispMSGDelegate(DispMsg);

    69.                 //使用控件lstMain的Invoke方法执行DMSGD代理(其类型是DispMSGDelegate)
    70.                 this.lstMain.Invoke(DMSGD, iIndex, strMsg);
    71.                
    72.             }
    73.         }

    74.         /// <summary>
    75.         /// 定义一个线程函数,用于循环向列表中添加数据
    76.         /// </summary>
    77.         private void Thread_DisplayMSG()
    78.         {
    79.             for (int i = 0; i < 10000; i++)
    80.             {
    81.                 DispMsg(i + 1, "Welcome you : " + (i + 1).ToString());
    82.                 Thread.Sleep(10);
    83.             }
    84.         }

    85.         /******************************* 定义该类的事件处理函数 ********************************/

    86.         /// <summary>
    87.         /// 【开始】按钮的单击事件处理函数,新建一个线程向窗体上的ListView控件填写内容
    88.         /// </summary>
    89.         /// <param name="sender"></param>
    90.         /// <param name="e"></param>
    91.         private void btnBegin_Click(object sender, EventArgs e)
    92.         {
    93.             //创建一个新的线程
    94.             Thread tWorkingThread = new Thread(Thread_DisplayMSG);

    95.             //将新建的线程加入到自定义线程队列中,以便在窗体结束时关闭所有的线程
    96.             ChaosThreadList.Add(tWorkingThread);

    97.             //开启线程
    98.             tWorkingThread.Start();
    99.         }     

    100.     }
    101. }
    复制代码

      这样子就可以实现用子线程去操作主线程窗体上的控件的内容,同时,又不影响主线程对窗体上其他控件的响应。
      点击[开始]按钮,程序开启一个新的线程,不断向列表中添加新的数据,而同时不会影响主界面对其它控件(例如:文本框)的响应。

      [P.S]:
       INVOKE方法的作用:
       它使该控件所在的线程执行Invoke方法参数中指定的代理,也就是使主线程执行我们想对控件进行的操作。
    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|Archiver|手机版|小黑屋|Silian Lighting+ ( 蜀ICP备14004521号-1 )

    GMT+8, 2024-5-19 03:30 , Processed in 1.046875 second(s), 24 queries .

    Powered by Discuz! X3.2

    © 2001-2013 Comsenz Inc.

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