在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         /// 【开始】按钮的单击事件处理函数,新建一个线程向窗体上的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 }

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

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

[P.S]:

INVOKE方法的作用:

它使该控件所在的线程执行Invoke方法参数中指定的代理,也就是使主线程执行我们想对控件进行的操作。

时间: 2024-08-03 07:15:54

在C#中子线程如何操作主线程中窗体上控件的相关文章

Android新线程中更新主线程中的UI控件

Android中的View都不是线程安全的,所以如果在某一个新线程中直接更新主线程中的UI控件时就会报如下错误: android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. 为了解决在另一个线程中更新UI控件的问题,我们可以使用如下几种解决方案: 1. 自己写Handler解决,参见<And

[Winform]线程间操作无效,从不是创建控件的线程访问它的几个解决方案,async和await?

目录 概述 取消跨线程检查 使用委托异步调用 sync和await 总结 概述 最近在qq群里有一朋友,问起在winform中怎么通过开启线程的方式去处理耗时的操作,比如,查看某个目录下所有的文件,或者符合要求的文件.下班回来,也研究了一下.发现多线程这块有点薄弱,也算是补一补吧. 在winform开发,经常会遇到需要在控件上加载大量数据(也就是常说的耗时操作),这会导致程序出现假死状态,这个时候我们就会想到线程. 在智能客户端应用程序中,这样的线程创建并管理用户界面 (UI),因而称为 UI

线程间操作无效:从不是创建控件的线程访问它的三种方法

访问 Windows 窗体控件本质上不是线程安全的.如果有两个或多个线程操作某一控件的状态,则可能会迫使该控件进入一种不一致的状态.还可能出现其他与线程相关的 bug,包括争用情况和死锁.确保以线程安全方式访问控件非常重要. 解决办法 1.把CheckForIllegalCrossThreadCalls设置为false 在多线程程序中,新创建的线程不能访问UI线程创建的窗口控件,如果需要访问窗口中的控件,可以在窗口构造函数中将CheckForIllegalCrossThreadCalls设置为

C# 线程调用主线程中的控件

由于项目的需要,最近几天一直在做串口和数据库.由于C#使用的时间不长,所以在编写代码和调试的过程中总是遇到意想不到的问题,比如在使用串口接收数据的时候,在接收数据事件中想把接收的数据放入一个textbox作显示,但是明明非常简单的代码,在编译的时候总是提示有错误.后来查看网上资料,才知道C#还有委托,匿名等等之类的新东西.下面我就把我这几天的经验和大家分享一下.这次就主要说说委托和匿名方法,以后在说说串口使用方面的经验. 先说一下委托的基本概念,委托是一种引用型的数据类型,其实它的概念和C语言的

网络操作不能直接写在主线程中 以及 为什么不能在子线程中更新UI控件的属性

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ //注意: 所有网络操作不能直接写在主线程中 因为所有的网络操作都是耗时的,如果加载到主线程中,会导致与用户的交互出现问题 ,所以要加载到子线程中 // [self loadImage]; [self performSelectorInBackground:@selector(loadImage) withObject:nil]; } //加

2. GCD的使用(线程间通信---子线程执行耗时操作/主线程更新UI)

// // ViewController.m // 07-线程间通信(加载图片,在主线程中更新UI) // // Created by Jasperay on 15/9/3. // Copyright (c) 2015年 @aLonelyRoot3. All rights reserved. // #import "ViewController.h" @interface ViewController () @property (weak, nonatomic) IBOutlet UI

用Handler的post()方法来传递线程中的代码段到主线程中执行

自定义的线程中是不能更新UI的,但是如果遇到更新UI的事情,我们可以用handler的post()方法来将更新UI的方法体,直接传送到主线程中,这样就能直接更新UI了.Handler的post()方法就是将Runnable中的代码段传送到主线程. 布局文件就是个textView,我就不贴了. 全部代码 package com.kale.handler; import android.app.Activity; import android.graphics.Color; import andro

Android子线程更新UI主线程方法之Handler

背景: 我们开发应用程序的时候,处于线程安全的原因子线程通常是不能直接更新主线程(UI线程)中的UI元素的,那么在Android开发中有几种方法解决这个问题,其中方法之一就是利用Handler处理的. 下面说下有关Handler相关的知识. 多线程一些基础知识回顾:在介绍Handler类相关知识之前,我们先看看在Java中是如何创建多线程的方法有两种:通过继承Thread类,重写Run方法来实现通过继承接口Runnable实现多线程 具体两者的区别与实现,看看这篇文章中的介绍:http://de

Android判断当前线程是否是主线程的方法

开发过程中有时候会在Thread类中执行某些操作,有些操作会由于Android版本的不同,尤其是低版本而Crash,因此必要的时候会查看某些容易引起crash的操作是否是在主线程,这里举三种方法: 方法一:使用Looper类判断 Looper.myLooper() == Looper.getMainLooper() 方法二:通过查看Thread类的当前线程 Thread.currentThread() == Looper.getMainLooper().getThread() Android判断