C#控件跨线程内容更新

在使用C#开发winform应用程序时,经常会碰到对控件跨线程访问造成的异常。在winform中UI线程和工作线程是分开的,但在实际使用中经常会需要在工作线程更新UI线程中创建的控件。

方法1:禁用跨线程访问控件检测

.NET默认开启了禁止跨线程控件访问,在程序中将其置为false取消跨线程访问检测即可实现跨线程访问。

代码中添加如下代码:

Control.CheckForIllegalCrossThreadCalls = false;

备注:该方法虽然可以实现跨线程访问,但同时也取消了线程之间冲突访问的检查,因此可能会存在多个线程对同一控件进行同时访问,此时该控件的值难以预料。因此,在实际使用非线程安全,不推荐使用!

方法2:使用delegate和Invoke/BeginInvoke

Invoke是同步的,它会等待工作线程完成

BeginInvoke是异步的,它会创建另外一个线程去完成工作线程

在使用委托时分为3步(委托有C语言中函数指针的意味):

定义声明委托--->实例化委托--->调用委托

1)定义声明委托

修饰符  delegate  返回值类型  委托名 ( 参数列表 );

2)实例化委托

委托名  委托对象名 = new 委托名 ( 方法名 );

委托中的方法民对应的方法的返回值和参数列表的类型和数目必须一致。

3)调用委托

委托对象名 ( 参数列表 );

#define USE_DELEGATE
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;namespace SerialPort_CRC8_CRC16
{public partial class Form1 : Form{
#if USE_DELEGATE/* 自定义委托,用于实现跨线程对控件的内容更新 */public delegate void MyControlInvoke(Control sender, string data);/// <summary>/// 更新控件内容/// </summary>/// <param name="control"></param>/// <param name="data"></param>public void UpdataControl(Control control, string data){//判断调用UpdataControl方法的线程和控件线程是否相同,不同则需使用Invoke方法,相同则直接操作if (control.InvokeRequired == true) {MyControlInvoke myControlInvoke = new MyControlInvoke(UpdataControl);//方法1:异步执行this.BeginInvoke(myControlInvoke, new object[] { control, data }); //方法2:同步执行//this.Invoke(myControlInvoke, new object[] { control, data });  }else   {control.Text = data;}}
#endif/// <summary>/// Form1类的构造函数/// </summary>public Form1(){InitializeComponent();serialPort1.DataReceived += new System.IO.Ports.SerialDataReceivedEventHandler(serialPort1_DataReceived);cbxPortNum.DataSource = System.IO.Ports.SerialPort.GetPortNames();btnOprtPort.BackColor = Color.OrangeRed;#if !USE_DELEGATEControl.CheckForIllegalCrossThreadCalls = false;    //不使用委托时关闭跨线程调用检测(非线程安全)#endifrbtnCRC16.Checked = true;}byte[] myDataByte = new byte[] { }; //发送数据缓冲区private void Calculate_CRC16(){CRC16 crc16 = new CRC16();string[] myDataStr = tBox1.Text.Split(' ');List<byte> Arc = new List<byte>();foreach (var item in myDataStr){try{Arc.Add(Convert.ToByte(item, 16));}catch (Exception){MessageBox.Show("请输入以空格为间隔的16进制字符串!");return;}}myDataByte = Arc.ToArray();tBox2.Clear();/* 发送该指令至TDM表头读取类别量程信息* 发送:xx 03 00 01 00 01 crcL crcH* 接收:xx 03 02 class range crcL crcH*/UInt16 crc16Res = crc16.Crc16_Modbus(myDataByte, (uint)myDataByte.Length);  //计算CRC校验和,crc_Data的计算结果为低字节在高位,高字节在低位Arc.Add((byte)(crc16Res >> 8));Arc.Add((byte)(crc16Res));myDataByte = Arc.ToArray();sendDataCount = Arc.Count;foreach (var item in myDataByte){tBox2.Text += (item.ToString("X02") + ' ');}}private void Calculate_CRC8(){myCRC mycrc = new myCRC();string[] myDataStr = tBox1.Text.Split(' ');List<byte> Arc = new List<byte>();foreach (var item in myDataStr){try{Arc.Add(Convert.ToByte(item, 16));}catch (Exception){MessageBox.Show("请输入以空格为间隔的16进制字符串!");return;}}myDataByte = Arc.ToArray();tBox2.Clear();byte res = mycrc.Crc8_init(0x00, myDataByte, (UInt16)(myDataStr.Length));Arc.Add(res);myDataByte = Arc.ToArray();sendDataCount = Arc.Count;foreach (var item in myDataByte){tBox2.Text += (item.ToString("X02") + ' ');}}private void button1_Click(object sender, EventArgs e){if (rbtnCRC8.Checked == true){Calculate_CRC8();}else if (rbtnCRC16.Checked == true){Calculate_CRC16();}}private void btnClear_Click(object sender, EventArgs e){
#if USE_DELEGATEthis.UpdataControl(tBox1, string.Empty);this.UpdataControl(tBox2, string.Empty);this.UpdataControl(tboxSend, string.Empty);this.UpdataControl(tboxRecv, string.Empty);
#elsetBox1.Text = string.Empty;tBox2.Text = string.Empty;tboxSend.Text = string.Empty;tboxRecv.Text = string.Empty;
#endif}private void tBox1_KeyDown(object sender, KeyEventArgs e){if (e.KeyCode == Keys.Enter){if (rbtnCRC8.Checked == true){Calculate_CRC8();}else if (rbtnCRC16.Checked == true){Calculate_CRC16();}}}public byte[] usartRecvBuffer = new byte[4096]; //开辟4096Byte的接收缓冲区private void serialPort1_DataReceived(object sender, System.IO.Ports.SerialDataReceivedEventArgs e){//if (e.EventType == System.IO.Ports.SerialData.Eof)if (serialPort1.BytesToRead == 0){return;}string str = string.Empty;stopWatch.Stop();TimeSpan timespan = stopWatch.Elapsed;str = timespan.TotalMilliseconds.ToString() + "mS";
#if USE_DELEGATEthis.UpdataControl(label6, str);
#elselabel6.Text = str;
#endif//串口接收并不是接收的每个字节都会进入该事件,因此需在该事件中接收完数据Int32 readByteNum = 0;/* 等待数据接收完成,即3mS内串口接收到的数据长度不再变化则认为数据已经接收完成 */do{readByteNum = serialPort1.BytesToRead;System.Threading.Thread.Sleep(10);} while (readByteNum < serialPort1.BytesToRead && serialPort1.BytesToRead < 4096);serialPort1.Read(usartRecvBuffer, 0, readByteNum);  //将串口缓冲区的数据保存至接收缓冲区serialPort1.DiscardInBuffer();                      //清空串口缓冲区的内容str = string.Empty;for (int i = 0; i < readByteNum; i++){str += (usartRecvBuffer[i].ToString("X02") + ' ');}
#if USE_DELEGATEthis.UpdataControl(tboxRecv, str);
#elsetboxRecv.Text = str;
#endif}public System.Diagnostics.Stopwatch stopWatch = new System.Diagnostics.Stopwatch();public int sendDataCount = 0;   //要发送的字节数/// <summary>/// “发送”按钮按下/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void btnSend_Click(object sender, EventArgs e){if (serialPort1.IsOpen == false){MessageBox.Show("发送数据前请先打开串口!", "提示");return;}if (sendDataCount == 0){MessageBox.Show("发送区为空!", "提示");return;}
#if USE_DELEGATEthis.UpdataControl(label6, "0mS");this.UpdataControl(tboxRecv, string.Empty);this.UpdataControl(tboxSend, tBox2.Text);
#elselabel6.Text = "0";tboxRecv.Text = string.Empty;tboxSend.Text = tBox2.Text;
#endifstopWatch.Restart();serialPort1.Write(myDataByte, 0, sendDataCount);  }private void btnOprtPort_Click(object sender, EventArgs e){if (btnOprtPort.Text == "单击打开串口"){try{//初始化并打开串口serialPort1.PortName = cbxPortNum.Text;serialPort1.BaudRate = 9600;serialPort1.DataBits = 8;serialPort1.Parity = System.IO.Ports.Parity.None;serialPort1.StopBits = System.IO.Ports.StopBits.One;serialPort1.ReceivedBytesThreshold = 1;serialPort1.Open();
#if USE_DELEGATEthis.UpdataControl(label6, "0mS");this.UpdataControl(tboxRecv, string.Empty);this.UpdataControl(tboxSend, tBox2.Text);this.UpdataControl(btnOprtPort, "单击关闭串口");
#elselabel6.Text = "0mS";tboxRecv.Text = string.Empty;tboxSend.Text = tBox2.Text;btnOprtPort.Text = "单击关闭串口";
#endifbtnOprtPort.BackColor = Color.GreenYellow;}catch (Exception){MessageBox.Show("串口打开失败!", "警告");}}else{try{serialPort1.Close();
#if USE_DELEGATEthis.UpdataControl(btnOprtPort, "单击打开串口");
#else btnOprtPort.Text = "单击打开串口";
#endifbtnOprtPort.BackColor = Color.OrangeRed;}catch (Exception){MessageBox.Show("串口关闭失败!", "警告");}}}private void Form1_FormClosing(object sender, FormClosingEventArgs e){if (serialPort1.IsOpen) //程序退出时需关闭已打开串口{try{serialPort1.Close();}catch (Exception){throw;}}}}
}

 

 

 

 

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/429507.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

document.addeventlistener方法不执行_JUnit 5 测试方法的执行优先级

前言使用单元测试有时对方法的执行顺序有要求&#xff0c;而默认情况下测试方法的执行并非按照编写顺序&#xff0c;这就导致测试用例因执行顺序而导致的不通过。这里我使用的JUnit版本是5.6.2&#xff0c;下面讲述如何自定义测试方法的执行优先级。TestMethodOrder这个注解标注…

2异常处理_Java处理异常2种机制关键字区别解析

在异常处理的过程中,throws和throw的区别是?throws:是在方法上对一个方法进行声明,而不进行处理,而是向上传,谁调用谁处理.throw:是在具体的抛出一个异常类型.throws的栗子:throws的话,就是这个方法有可能会产生异常,而我只是将它声明出去,我自己不处理,如果有人调用的时候,可…

TL431的用法

TL431是可控精密稳压源。它的输出电压用两个电阻就可以设置从Vref&#xff08;2.5V&#xff09;到36V范围内的任何值。该器件的典型动态阻抗为0.2Ω&#xff0c;在很多应用中用它代替稳压二极管&#xff0c;例如&#xff0c;数字电压表&#xff0c;运放电路&#xff0c;可调压电…

AD16原理图.schdot中批量修改标签中的文本字体、大小、颜色

1&#xff09;使用鼠标左键区域选中需要修改的文本&#xff0c;或按住shift键使用鼠标左键选择分布在图中各处的需要修改的文本 2&#xff09;按住shift键&#xff0c;鼠标左键双击选中的任一文本&#xff0c;弹出如下的SCH Inspector弹窗 3&#xff09;在SCH Inspector弹窗中…

12v小型电机型号大全_鄂破碎机型号大全图,小型鄂破碎机价格

鄂破碎机&#xff0c;粗碎加工主力配置&#xff0c;因为使用频繁&#xff0c;很多案例都少不了&#xff0c;所以设备生产厂家有很多&#xff0c;但是随时鄂破碎机的机型更新&#xff0c;核心性能越来越完善&#xff0c;整机时产更高&#xff0c;而且环保性能更强悍。鄂破碎机型…

我的第一次Pascal程序

今天刚刚来学点Pascal语言&#xff0c;都怪我买的书大多是用Pascal语言写的&#xff0c;所以我只能试着学一点咯&#xff01; 来一段程序员最经典的Hello World&#xff01; 1 program exl_2; 2 begin 3 write(Hello World!); 4 end. 转载于:https://www.cnblogs.com/ECJTU…

C#中的前台线程和后台线程的区别

前台线程&#xff1a;应用程序必须运行完所有的前台线程才能退出&#xff0c;默认创建的线程都是前台线程。 后台线程&#xff1a;应用程序可以不必考虑后台线程是否已经运行完毕&#xff08;包括正常退出和异常退出&#xff09;&#xff0c;只要所有的前台线程结束&#xff0…

C#中string.Concat方法的使用

string.Concat方法用于连接string的一个或多个实例&#xff0c;或string的一个或多个实例的object表示形式 https://docs.microsoft.com/zh-cn/dotnet/api/system.string.concat?viewnet-5.0 1&#xff09;连接string[]或object[]数组中的元素 using System; using System.C…

将java项目做成app_将鸿蒙做成和EMUI一致,又兼容安卓APP,华为已立于不败之地...

自从华为的系统曝光以来&#xff0c;每天都有众多的网友为其摇旗呐喊&#xff0c;说不管怎么时候推出&#xff0c;甚至就算不好用&#xff0c;都无条件支持&#xff0c;说如果连自己人都不支持&#xff0c;就不可能成功了。但近日&#xff0c;随着网上曝光华为鸿蒙系统的的相关…

C# 使用StackFrame和StackTrace类 获取当前函数名,当前代码行,源代码文件

https://docs.microsoft.com/zh-cn/dotnet/api/system.diagnostics.stackframe?viewnetframework-3.0 https://docs.microsoft.com/zh-cn/dotnet/api/system.diagnostics.stacktrace?viewnetframework-3.0 测试代码如下&#xff1a; using System; using System.Collections…

2017-03-16 Codeforces 453A 概率期望,思维 UOJ 228(待补)

Codeforces 453A A. Little Pony and Expected Maximum 题意&#xff1a;一个m面质地均匀的骰子&#xff0c;每面出现的概率都是独立的1/m, 你需要投掷n次&#xff0c;其结果是这n次出现的最大点数。问投掷n次骰子的结果的期望值是多少&#xff0c;要求相对误差或绝对误差不…

vuex的命名空间有哪些_Vue 3 带来的 Vuex 的替代方案

一、前言就像是 React 社区在 HOOK API 出现后很快就使用 useReducer、useContext 代替了 Redux 进行状态管理一样。Vue3 也是时候抛弃 Vuex 进行状态管理了。在考虑为什么要抛弃 Vuex 之前&#xff0c;我们先来想一下为什么要引入 Vuex&#xff1f;Vuex 实际上解决的问题是「组…

C# Task异步编程

1、不适用异步的示例 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Threading; using System.Net; using System.Diagnostics;namespace ConsoleApplication1 {class MyDownloadStrin…

人形图案c语言程序_最多 280 字符,你能用 Basic 玩出哪些花样程序来?

(给程序员的那些事加星标)转自&#xff1a;机器之心【导读】&#xff1a;推特与计算机能擦出什么样的火花呢&#xff1f;大多数人可能就想到在计算机上发推特呗。但是&#xff0c;有人就不这么想。酷爱计算机演进史和推特的 Dominic Pajak 创建了 BBC Micro Bot&#xff0c;它能…

???--???二进制变换

题意 &#xff1a; 定义两种变换 1 &#xff1a; i i - 1 2 &#xff1a; i i - lowbit (i) 定义函数Calc(i,j)为二进制意义下 i 变换到 j 的最小步数。 给你一个二进制整数 n&#xff0c;要求 sigma {(i 1 -> n) sigma {(j 0 -> i - 1) Calc (i,j)}} 数据范围 : 令…

C# 派生类的构造函数

假定没有为任何类定义任何显式的构造函数,这样编译器就会为所有的类提供默认的初始化构 造函数,在后台会进行许多操作,但编译器可以很好地解决类的层次结构中的所有问题,每个类中 的每个字段都会初始化为对应的默认值。但在添加了一个我们自己的构造函数后,就要通过派生类 的层…

js 获取url问号前_PHP获取指定网页的HTML代码并执行输出

PHP获取指定网页的HTML代码并执行输出&#xff0c;这个方法主要是将所要或取目标的URL地址的网站中获取相关内容到自己的网页中。代码如下&#xff1a;<?php $srcurl "所要截取目标的URL地址"; $handle fopen($srcurl,"rb"); $content fread($handl…

AD16画线时如何切换90°、45°、任意角度画线模式

在绘图界面选择画线后&#xff0c;使用“shift空格”可切换不同的画线模式。切换过程中会有90模式、45度模式、任意角度模式等&#xff0c;在这些模式中可使用空格键在进行细分切换。 1、90模式 2、45模式 3、任意模式

1200兆路由器网速_如何选购路由器才能发挥宽带的网速?

很多人多少都会遇到家里宽带网速慢的时候&#xff0c;家中明明是光纤宽带&#xff0c;可是网速却没有想象中的那么快&#xff1f;尤其是宽带的带宽升级到100M、200M、500M的时候&#xff0c;感觉跟没有提速一样。也许你家的路由器该换新啦&#xff01;那么&#xff0c;想要选购…

C# 静态类

------《C#高级编程》第7版