C#C#中SerialPort类中DataReceived事件GUI实时处理方法

MSDN:从 SerialPort 对象接收数据时,将在辅助线程上引发 DataReceived 事件。由于此事件在辅助线程而非主线程上引发,因此尝试修改主线程中的一些元素(如 UI 元素)时会引发线程异常。如果有必要修改主 Form 或 Control 中的元素,必须使用 Invoke 回发更改请求,这将在正确的线程上执行.进而要想将辅助线程中所读到的数据显示到主线程的Form控件上时,只有通过Invoke方法来实现 
下面是代码实例: 

[c-sharp] view plain copy
  1. private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)  
  2. {  
  3.    int SDateTemp = this.serialPort1.ReadByte();  
  4.    //读取串口中一个字节的数据  
  5.    this.tB_ReceiveDate.Invoke(     
  6.     //在拥有此控件的基础窗口句柄的线程上执行委托Invoke(Delegate)  
  7.     //即在textBox_ReceiveDate控件的父窗口form中执行委托.  
  8.     new MethodInvoker(              
  9.     /*表示一个委托,该委托可执行托管代码中声明为 void 且不接受任何参数的任何方法。 在对控件的 Invoke    方法进行调用时或需要一个简单委托又不想自己定义时可以使用该委托。*/  
  10.     delegate{                   
  11.     /*匿名方法,<a href="http://lib.****.net/base/csharp" class='replace_word' title="C#知识库" target='_blank' style='color:#df3434; font-weight:bold;'>C#</a>2.0的新功能,这是一种允许程序员将一段完整代码区块当成参数传递的程序代码编写技术,通过此种方法可    以直接使用委托来设计事件响应程序以下就是你要在主线程上实现的功能但是有一点要注意,这里不适宜处理过多的方法,因为C#消息机制是消息流水线响应机制,如果这里在主线程上处理语句的时间过长会导致主UI线程阻塞,停止响应或响应不顺畅,这时你的主form界面会延迟或卡死      */                     
  12.     this.tB_ReceiveDate.AppendText(SDateTemp.ToString());//输出到主窗口文本控件  
  13.     this.tB_ReceiveDate.Text += " ";}  
  14.     )  
  15.     );  
  16. }  

 

1.首先通过按键创建子线程:

创建子线程,子线程调用changeText方法。

C#C#中SerialPort类中DataReceived事件GUI实时处理方法
1      private void btnOK_Click(object sender, EventArgs e)
2      {
3         Thread th = new Thread(changeText);
4         th.Name = "new Thread!";
5         th.IsBackground = true;
6         th.Start();
7      }
C#C#中SerialPort类中DataReceived事件GUI实时处理方法

2.子线程操作弹窗提示:

1      void changeText()
2      {
3          Thread.Sleep(3000);
4          MessageBox.Show("进入子线程:"+Thread.CurrentThread.Name);
5      }

效果图:

C#C#中SerialPort类中DataReceived事件GUI实时处理方法

 

3.子线程修改主线程控件

(1)错误的直接使用:

1     void changeText()
2     {
3         Thread.Sleep(3000);
4         textBox1.Text = "进入子线程!";
5     }

效果图:

C#C#中SerialPort类中DataReceived事件GUI实时处理方法

 

 

(2)关闭跨线程的检查:

1       CheckForIllegalCrossThreadCalls = false;

效果图:

C#C#中SerialPort类中DataReceived事件GUI实时处理方法

 

(3)对跨线程进行检查

声明委托:

注:参数类型是object型

1     public delegate void changeTextHandler(object str);

按键点击调用方法修改控件:

注:参数不是在线程创建的时候写入,是在start函数写入!

C#C#中SerialPort类中DataReceived事件GUI实时处理方法
1     private void btnOK_Click(object sender, EventArgs e)
2     {
3         Thread th = new Thread(changeText);
4         th.Name = "new Thread!";
5         th.IsBackground = true;
6         th.Start("进入子线程!");
7     }
C#C#中SerialPort类中DataReceived事件GUI实时处理方法

修改控件的方法(带一个参数):

C#C#中SerialPort类中DataReceived事件GUI实时处理方法
 1     void changeText(object str)
 2     {
 3         if (textBox1.InvokeRequired == true)
 4         {
 5              changeTextHandler ct = new changeTextHandler(changeText);   
 6              textBox1.Invoke(ct, new object[] { str });
 7         }
 8         else
 9         {
10              textBox1.Text = str.ToString();
11         }
12     }
C#C#中SerialPort类中DataReceived事件GUI实时处理方法

效果图:

C#C#中SerialPort类中DataReceived事件GUI实时处理方法

 

(4)使用MethodInvoker解决跨线程问题

  •   MethodInvoker是无参无返回值;
  •   Action是多参无返回;
  •   Func是多参又返回。
C#C#中SerialPort类中DataReceived事件GUI实时处理方法
1      private void btnOK_Click(object sender, EventArgs e)
2      {
3          Thread th = new Thread(changeText);
4          th.Name = "new Thread!";
5          th.IsBackground = true;
6          th.Start();
7      }
C#C#中SerialPort类中DataReceived事件GUI实时处理方法

调用对事件的处理方法:

1     void changeText()
2     {
3          Thread.Sleep(3000);
4          MethodInvoker ln = new MethodInvoker(change);
5          this.BeginInvoke(ln);
6     }

调用对控件的修改方法:

1         void change()
2         {
3             textBox1.Text = "进入子线程!";
4         }

效果图:

C#C#中SerialPort类中DataReceived事件GUI实时处理方法