C#控件跨线程内容更新
在使用C#開發(fā)winform應用程序時,經(jīng)常會碰到對控件跨線程訪問造成的異常。在winform中UI線程和工作線程是分開的,但在實際使用中經(jīng)常會需要在工作線程更新UI線程中創(chuàng)建的控件。
方法1:禁用跨線程訪問控件檢測
.NET默認開啟了禁止跨線程控件訪問,在程序中將其置為false取消跨線程訪問檢測即可實現(xiàn)跨線程訪問。
代碼中添加如下代碼:
Control.CheckForIllegalCrossThreadCalls = false;
備注:該方法雖然可以實現(xiàn)跨線程訪問,但同時也取消了線程之間沖突訪問的檢查,因此可能會存在多個線程對同一控件進行同時訪問,此時該控件的值難以預料。因此,在實際使用非線程安全,不推薦使用!
方法2:使用delegate和Invoke/BeginInvoke
Invoke是同步的,它會等待工作線程完成
BeginInvoke是異步的,它會創(chuàng)建另外一個線程去完成工作線程
在使用委托時分為3步(委托有C語言中函數(shù)指針的意味):
定義聲明委托--->實例化委托--->調(diào)用委托
1)定義聲明委托
修飾符? delegate? 返回值類型? 委托名 ( 參數(shù)列表 );
2)實例化委托
委托名? 委托對象名 = new 委托名 ( 方法名 );
委托中的方法民對應的方法的返回值和參數(shù)列表的類型和數(shù)目必須一致。
3)調(diào)用委托
委托對象名 ( 參數(shù)列表 );
#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/* 自定義委托,用于實現(xiàn)跨線程對控件的內(nèi)容更新 */public delegate void MyControlInvoke(Control sender, string data);/// <summary>/// 更新控件內(nèi)容/// </summary>/// <param name="control"></param>/// <param name="data"></param>public void UpdataControl(Control control, string data){//判斷調(diào)用UpdataControl方法的線程和控件線程是否相同,不同則需使用Invoke方法,相同則直接操作if (control.InvokeRequired == true) {MyControlInvoke myControlInvoke = new MyControlInvoke(UpdataControl);//方法1:異步執(zhí)行this.BeginInvoke(myControlInvoke, new object[] { control, data }); //方法2:同步執(zhí)行//this.Invoke(myControlInvoke, new object[] { control, data }); }else {control.Text = data;}} #endif/// <summary>/// Form1類的構(gòu)造函數(shù)/// </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; //不使用委托時關(guān)閉跨線程調(diào)用檢測(非線程安全)#endifrbtnCRC16.Checked = true;}byte[] myDataByte = new byte[] { }; //發(fā)送數(shù)據(jù)緩沖區(qū)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();/* 發(fā)送該指令至TDM表頭讀取類別量程信息* 發(fā)送: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的計算結(jié)果為低字節(jié)在高位,高字節(jié)在低位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的接收緩沖區(qū)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//串口接收并不是接收的每個字節(jié)都會進入該事件,因此需在該事件中接收完數(shù)據(jù)Int32 readByteNum = 0;/* 等待數(shù)據(jù)接收完成,即3mS內(nèi)串口接收到的數(shù)據(jù)長度不再變化則認為數(shù)據(jù)已經(jīng)接收完成 */do{readByteNum = serialPort1.BytesToRead;System.Threading.Thread.Sleep(10);} while (readByteNum < serialPort1.BytesToRead && serialPort1.BytesToRead < 4096);serialPort1.Read(usartRecvBuffer, 0, readByteNum); //將串口緩沖區(qū)的數(shù)據(jù)保存至接收緩沖區(qū)serialPort1.DiscardInBuffer(); //清空串口緩沖區(qū)的內(nèi)容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; //要發(fā)送的字節(jié)數(shù)/// <summary>/// “發(fā)送”按鈕按下/// </summary>/// <param name="sender"></param>/// <param name="e"></param>private void btnSend_Click(object sender, EventArgs e){if (serialPort1.IsOpen == false){MessageBox.Show("發(fā)送數(shù)據(jù)前請先打開串口!", "提示");return;}if (sendDataCount == 0){MessageBox.Show("發(fā)送區(qū)為空!", "提示");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, "單擊關(guān)閉串口"); #elselabel6.Text = "0mS";tboxRecv.Text = string.Empty;tboxSend.Text = tBox2.Text;btnOprtPort.Text = "單擊關(guān)閉串口"; #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("串口關(guān)閉失敗!", "警告");}}}private void Form1_FormClosing(object sender, FormClosingEventArgs e){if (serialPort1.IsOpen) //程序退出時需關(guān)閉已打開串口{try{serialPort1.Close();}catch (Exception){throw;}}}} }?
?
?
?
?
?
總結(jié)
以上是生活随笔為你收集整理的C#控件跨线程内容更新的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: matlab所有画图函数,matlab所
- 下一篇: C# 静态类