C#多线程应用:子线程更新主窗体控件的值(二)
在上篇文章中,我已经给大家列了一个在主线程中实现的方式,这篇文章来给大家说说使用Invoke的方式的例子:
对于不代理不太熟悉的朋友,建议先查查相关资料;
例子一:
在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 }

这样子就可以实现用子线程去操作主线程窗体上的控件的内容,同时,又不影响主线程对窗体上其他控件的响应。程序运行截图如下:
点击[开始]按钮,程序开启一个新的线程,不断向列表中添加新的数据,而同时不会影响主界面对其它控件(例如:文本框)的响应。
[P.S]:
INVOKE方法的作用:
它使该控件所在的线程执行Invoke方法参数中指定的代理,也就是使主线程执行我们想对控件进行的操作。
出处:http://www.cnblogs.com/chaosimple/archive/2012/05/11/2495811.html
--------------------------------------------------------------------------------------------------------------------
例子二:
本文将详细介绍C#利用子线程如何刷新主线程,需要了解更多的朋友可以参考下
要求:如下图,使用线程操作
1、实时显示当前时间
2、输入加数和被加数,自动出现结果
分析:两个问题解决的方式相同,使用子线程进行时间操作和加法操作,然后刷新主线程的控件显示结果
复制代码 代码如下:
using System;
using System.Threading;
using System.Windows.Forms;
namespace WinThread
{
public partial class frmMain : Form
{
public frmMain()
{
InitializeComponent();
}
/// <summary>
/// 初始化
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void frmMain_Load(object sender, EventArgs e)
{
// 控件初始化
this.txtOne.Text = "0";
this.txtSecond.Text = "0";
// 显示时间操作
Thread showTimeThread = new Thread(new ThreadStart(GetTime));
showTimeThread.IsBackground = true;
showTimeThread.Start();
// 加法操作
Thread addThread = new Thread(new ThreadStart(Add));
addThread.IsBackground = true;
addThread.Start();
}
#region 显示时间操作
/// <summary>
/// 取得实时时间
/// </summary>
private void GetTime()
{
try
{
while (true)
{
string currentTime = string.Format("{0}.{1}", DateTime.Now.ToLongTimeString(), DateTime.Now.Millisecond);
ShowTime(currentTime);
Application.DoEvents();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
// 定义显示时间操作委托,用于回调显示时间方法
delegate void ShowTimeCallBack(string str);
/// <summary>
/// 实时显示时间
/// </summary>
/// <param name="str"></param>
private void ShowTime(string str)
{
if (this.txtCurrentTime.InvokeRequired)
{
ShowTimeCallBack showTimeCallBack = new ShowTimeCallBack(ShowTime);
this.Invoke(showTimeCallBack, new object[] { str });
}
else
{
this.txtCurrentTime.Text = str;
}
}
#endregion
#region 加法操作
/// <summary>
/// 加法操作
/// </summary>
private void Add()
{
try
{
while (true)
{
int i = Convert.ToInt32(this.txtOne.Text.Trim());
int j = Convert.ToInt32(this.txtSecond.Text.Trim());
ShowResult((i + j).ToString());
Application.DoEvents();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
// 定义加法操作委托,用于回调加法操作方法
delegate void ShowResultCallBack(string result);
/// <summary>
/// 显示结果
/// </summary>
/// <param name="result"></param>
private void ShowResult(string result)
{
if (this.txtResult.InvokeRequired)
{
// 写法1
//ShowResultCallBack showResultCallBack = new ShowResultCallBack(ShowResult);
//this.Invoke(showResultCallBack, new object[] { result });
// 写法2
//使用委托来赋值
this.txtResult.Invoke(
//委托方法
new ShowResultCallBack(ShowResult),
new object[] { result });
}
else
{
this.txtResult.Text = result;
}
}
#endregion
}
}
是不是很简单呢?
出处参考:http://www.jb51.net/article/32052.htm
C#多线程应用:子线程更新主窗体控件的值(二)的更多相关文章
- C#多线程应用:子线程更新主窗体控件的值(一)
我记得以前写过一次关于多线程的调用及更新的文章,由于时间比较久了,现在一时没找到.在做项目的时候,用到了多线程,还是有很多的同事在问多线程更新主窗体的事情,现在就这个事情做个记录. 说起多线程之间的更 ...
- c#中跨线程调用windows窗体控件
c#中跨线程调用windows窗体控件解决. 我们在做winform应用的时候,大部分情况下都会碰到使用多线程控制界面上控件信息的问题.然而我们并不能用传统方法来做这个问题,下面我将详细的介绍.首先来 ...
- c#中如何跨线程调用windows窗体控件
c#中如何跨线程调用windows窗体控件? 我们在做winform应用的时候,大部分情况下都会碰到使用多线程控制界面上控件信息的问题.然而我们并不能用传统方法来做这个问题,下面我将详细的介绍.首 ...
- 如何跨线程调用Windows窗体控件
通过一个子线程来操作主线程中的控件,但是,这样作会出现一个问题(如图1所示),就是TextBox控件是在主线程中创建的,在子线程中并没有对其进行创建,也就是从不是创建控件的线程访问它.那么,如何解决跨 ...
- Android子线程更新主界面
学习什么的还是要真正的有应用,有需求才能在最短的时间里面牢牢掌握一项技术. 今天就是这样的,产品一个需求下来,十万火急啊.然后之前只稍稍接触过,只能硬着头皮上了.最后牢牢地掌握了最简单的Handler ...
- C#多线程开发中如何更新UI界面控件内容
子线程不能修改UI线程的状态(比如文本框里面的内容). 解决的办法是写一个用来更新文本框内容的函数,然后在Worker线程里面通过BeginInvoke来利用delegate调用这个函数更新文本框. ...
- c#中如何跨线程调用windows窗体控件?
我们在做winform应用的时候,大部分情况下都会碰到使用多线程控制界面上控件信息的问题.然而我们并不能用传统方法来做这个问题,下面我将详细的介绍.首先来看传统方法: public partial c ...
- 在子线程更新主线程的UI组件
1.实例化一个 private Handler handlerBublishTopic = new Handler(Looper.getMainLooper()); 2. handlerBublish ...
- (转载)c# winform 用子窗体刷新父窗体,子窗体改变父窗体控件的值
第一种方法: 用委托,Form2和Form3是同一组 Form2 C#代码 using System; using System.Collections.Generic; using System.C ...
随机推荐
- 20155333 2016-2017-2 《Java程序设计》第七周学习总结
20155333 2016-2017-2 <Java程序设计>第七周学习总结 教材学习内容总结 Lambda 教材的引入循序渐近.深入浅出 Lambda去重复,回忆DRY原则 Lambda ...
- laravel 项目部署注意事项
1.'Failed to open stream: Permission denied' error - Laravel Laravel >= 5.4 php artisan cache:cle ...
- 用通俗的语言解释restful
实现了REST规范的Web API就叫RESTful API. 简单来说:就是用url定位资源,用http描述来操作资源. web是什么:分布式信息系统为超文本文件和其他对象(资源)提供访问入口. 资 ...
- Web前端学习笔记之前端跨域知识总结
0x00 前言 相信每一个前端er对于跨域这两个字都不会陌生,在实际项目中应用也是比较多的.但跨域方法的多种多样实在让人目不暇接.老规矩,碰到这种情况,就只能自己总结一篇博客,作为记录. 0x01 什 ...
- Web安全之BurpSuite抓取HTTPS请求
出现了问题,第一步要干什么呢? 当然是要去官方网站去找FAQ和help,先来练习一下英语 https://portswigger.net/burp/help/proxy_options_install ...
- openwrt生成的交叉编译器在哪里
答:在staging_dir目录下,示例如下: 编译一个arm64架构所生成的编译器在staging_dir/toolchain-aarch64_generic_gcc-7.4.0_musl/bin/ ...
- git如何在自动生成补丁时指定补丁名的起始编号
答:使用选项--start-number,用法如下: git format-patch 1f43be --start-number=2 这样就可以生成起始编号为2的补丁名,类似0002-me.patc ...
- luogu P1141 01迷宫
https://www.luogu.org/problem/show?pid=1141 还不太会用 BFS 然后就跟着感觉走了一波 经历了很多错误 刚开始的读入 然后BFS的过程 最后T三个点 看到别 ...
- Linux配置NFS实现共享
(1)安装相应rpm包 sudo rpm -ivh nfs-utils-1.2.3-70.el6.x86_64.rpm (2)配置共享目录:sudo vim /etc/exports /app/sof ...
- Spring IOC 源码简单分析 04 - bean的初始化
### 准备 ## 目标 了解 Spring 如何初始化 bean 实例 ##测试代码 gordon.study.spring.ioc.IOC04_Initialization.java publ ...