我遇到的问题类似于“在文本框中显示序列 - 交叉线程操作”这个主题,但我不明白给出的答案。
根据我发现的教程视频示例,我创建了一个非常简单的C#表单来发送/接收串行数据。效果很好,但该示例要求您单击按钮以接收数据,而我希望现在通过更新“已接收”文本框自动显示所接收的内容。
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的内容,但是在我(非程序员)的答案中没有足够的上下文来弄清楚如何实现,而且通常答案都在地图上并且没有达成共识这是正确/最好的。有人可以提供一个简单的解决方案,我需要添加到我的代码中以使其工作吗? 感谢。
答案 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不执行,除非您使用返回值。把我的回答作为介绍多线程同步,通信,交互的大而广阔的世界。祝你好运。