如何使用收到的串口字符串更新C#windows窗体文本框?

时间:2017-07-24 18:47:40

标签: c# multithreading winforms textbox serial-port

我遇到的问题类似于“在文本框中显示序列 - 交叉线程操作”这个主题,但我不明白给出的答案。

根据我发现的教程视频示例,我创建了一个非常简单的C#表单来发送/接收串行数据。效果很好,但该示例要求您单击按钮以接收数据,而我希望现在通过更新“已接收”文本框自动显示所接收的内容。

image of Form1

Program.cs由VSE2015生成:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace spcontrol
{
  static class Program
  {
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
      Application.EnableVisualStyles();
      Application.SetCompatibleTextRenderingDefault(false);
      Application.Run(new Form1());
    }
  }
}

Form1.cs显示基于教程示例的工作代码(即必须单击“接收”按钮):

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;
using System.IO.Ports;

namespace spcontrol
{
  public partial class Form1 : Form
  {

    public Form1()
    {
      InitializeComponent();
      GetAvailablePorts();
      //serialPort1.DataReceived += new SerialDataReceivedEventHandler(port_OnReceiveData);
    }

    void GetAvailablePorts()
    {
      string[] ports = SerialPort.GetPortNames();
      comboBox_portnames.Items.AddRange(ports);
    }

    private void button_openport_Click(object sender, EventArgs e)
    {
      try
      {
        if (comboBox_portnames.Text == "" || comboBox_baudrate.Text == "")
        {
          textBox_received.Text = "Please select port settings.";
        }
        else
        {
          textBox_received.Text = "";
          serialPort1.PortName = comboBox_portnames.Text;
          serialPort1.BaudRate = Convert.ToInt32(comboBox_baudrate.Text);
          serialPort1.Open();
          button_send.Enabled = true;
          button_receive.Enabled = true;
          textBox_sent.Enabled = true;
          button_openport.Enabled = false;
          button_closeport.Enabled = true;
          comboBox_portnames.Enabled = false;
          comboBox_baudrate.Enabled = false;
          serialPort1.DataBits = 8;
          serialPort1.Parity = Parity.None;
          serialPort1.StopBits = StopBits.One;
          serialPort1.Handshake = Handshake.None;
          label_config.Text = serialPort1.PortName + " " + serialPort1.BaudRate + " 8N1 None";
          progressBar_status.Value = progressBar_status.Maximum;
        }
      }
      catch (UnauthorizedAccessException)
      {
        textBox_received.Text = "Unauthorized access.";
      }
    }

    private void button_closeport_Click(object sender, EventArgs e)
    {
      serialPort1.Close();
      button_send.Enabled = false;
      button_receive.Enabled = false;
      textBox_sent.Enabled = false;
      button_openport.Enabled = true;
      button_closeport.Enabled = false;
      comboBox_portnames.Enabled = true;
      comboBox_baudrate.Enabled = true;
      progressBar_status.Value = progressBar_status.Minimum;
    }

    private void button_send_Click(object sender, EventArgs e)
    {
      serialPort1.WriteLine(textBox_sent.Text);
      textBox_sent.Text = "";
      textBox_sent.Focus();
    }

    private void button_receive_Click(object sender, EventArgs e)
    {
      try
      {
        textBox_received.Text = serialPort1.ReadLine();
      }
      catch (TimeoutException)
      {
        textBox_received.Text = "Timeout exception.";
      }
    }

    //private void port_OnReceiveData(object sender, SerialDataReceivedEventArgs e)
    //{
    //  SerialPort sp = (SerialPort)sender;
    //  textBox_received.Text += sp.ReadExisting();
    //}

  }
}

注释掉的代码是我尝试自动化收到的数据文本框显示(取消注释此代码并注释掉“button_receive_Click”函数中的“try”语句。)

但这样做会给我带来跨线程错误:

  

抛出异常:'System.InvalidOperationException'   System.Windows.Forms.dll未处理的类型异常   发生'System.InvalidOperationException'   System.Windows.Forms.dll其他信息:跨线程   操作无效:控制从线程访问的'textBox_received'   除了它创建的线程。

我已经阅读过有关使用Invoke或BeginInvoke的内容,但是在我(非程序员)的答案中没有足够的上下文来弄清楚如何实现,而且通常答案都在地图上并且没有达成共识这是正确/最好的。有人可以提供一个简单的解决方案,我需要添加到我的代码中以使其工作吗? 感谢。

2 个答案:

答案 0 :(得分:0)

private void port_OnReceiveData(object sender, SerialDataReceivedEventArgs e) {
    SerialPort sp = (SerialPort)sender;
    UpdateTextBox (sp.ReadExisting());
}

public void UpdateTextBox(string value) {
    if (InvokeRequired)
    {
        this.Invoke(new Action<string>(UpdateTextBox), new object[] {value});
        return;
    }
    textBox_received.Text += value;
}

答案 1 :(得分:0)

要理解这个问题,UI表单有自己的线程,UI特定的线程,为了使事情安全和简单,UI中的所有内容都在其线程的消息循环中处理。来自串口或其他通信通道的事件等事件通常安排在线程池或UI线程外的特定线程上。你需要的是跨越其他线程和UI线程之间的界限。在Windows窗体上有Form实例,Invoke和BeginInvoke的方法,它们将为UI消息循环安排一些工作,并且自己时间的UI线程将执行它。同步调用等待执行完成,BeginInvoke不执行,除非您使用返回值。把我的回答作为介绍多线程同步,通信,交互的大而广阔的世界。祝你好运。