jpc2112 jpc2112 - 3 years ago 301
C# Question

how to update C# windows form textbox with received serial port string?

I have a problem similar to the topic "Display serial in to textbox - Cross thread operation" but I don't understand the answers given for that.

I created a very simple C# form to send/receive serial data, based on a tutorial video example I found. Works great, but the example requires you to click a button to receive data, whereas I want to have it now automatically display whatever is received by updating the "Received" textbox.

image of Form1

Program.cs is generated by 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 shows working code based on the tutorial example (i.e. "Receive" button must be clicked):

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();
//}

}
}


The commented-out code is my attempt at automating the received data textbox display (uncomment this code and comment out the "try" statement within "button_receive_Click" function.)

But doing so gives me the cross-thread error:


Exception thrown: 'System.InvalidOperationException' in
System.Windows.Forms.dll An unhandled exception of type
'System.InvalidOperationException' occurred in
System.Windows.Forms.dll Additional information: Cross-thread
operation not valid: Control 'textBox_received' accessed from a thread
other than the thread it was created on.


I've read things about using Invoke or BeginInvoke but there's never enough context in the answers for me (a non-programmer) to figure out how to implement, plus usually the answers are all over the map and there's no consensus for which is correct/best. Can someone please provide a simple solution for what I need to add to my code to make this work?
Thanks.

Answer Source
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;
}
Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download