【问题标题】:how to update C# windows form textbox with received serial port string? [duplicate]如何使用接收到的串口字符串更新 C# windows 窗体文本框? [复制]
【发布时间】:2017-07-24 18:47:39
【问题描述】:

我有一个类似于“在文本框中显示序列 - 跨线程操作”主题的问题,但我不明白为此给出的答案。

根据我找到的教程视频示例,我创建了一个非常简单的 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 的内容,但对于我(非程序员)来说,答案中没有足够的上下文来弄清楚如何实现,而且通常答案都在地图上并且没有达成共识这是正确的/最好的。有人可以为我需要添加到我的代码中以使其工作的内容提供一个简单的解决方案吗? 谢谢。

【问题讨论】:

    标签: c# multithreading winforms textbox serial-port


    【解决方案1】:
    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;
    }
    

    【讨论】:

      【解决方案2】:

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

      【讨论】:

      • ipavlu,感谢您的解释。 lhdina 分享的代码解决了我的问题。我没有在这个 UI 中做任何其他处理,所以我想 Invoke 方法对我来说已经足够了。
      猜你喜欢
      • 2019-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-12-05
      • 1970-01-01
      • 2013-01-27
      • 1970-01-01
      相关资源
      最近更新 更多