Yes, you should use threading, assuming the ReadLine call on the serial port is blocking (most external I/O calls are). Lukeer's answer is one way to do it. As I mention in the comment to that, I would do this the 'old fashioned' way on the basis that (i) you only ever need one thread and (ii) that thread should be persistent and manage a port consistently through its lifetime.
I would actually have the thread constantly reading from the serial port and notifying the UI of that information, all start/stop would do would be set a flag in the UI as to whether it does anything with that information. Since you are just providing a reader to data which is being sent either way, not having Start/Stop pass those on to the device, this seems more natural to me. You can implement that later if necessary.
So I'd have a custom thread class which broadcasts the information you need:
public class SerialReaderThread {
private Thread t;
public SerialReaderThread() {
t = new Thread(RunMethod);
}
public void Start() { t.Start(); }
public void Stop() { t.Stop(); }
public event EventHandler<DataEventArgs> DataReceived;
private bool closed = false;
public void Close() { closed = true; }
private void RunMethod(){
SerialPort mySerialPort = new SerialPort();
mySerialPort.BaudRate = 9600;
mySerialPort.Parity = Parity.None;
mySerialPort.StopBits = StopBits.One;
mySerialPort.DataBits = 8;
mySerialPort.Handshake = Handshake.None;
mySerialPort.Open();
while(!closed){
string line = mySerialPort.ReadLine();
if(DataReceived != null) DataReceived(this, new DataEventArgs(line));
}
}
}
public class DataEventArgs : EventArgs {
public string Data { get; private set; }
public DataEventArgs(string data) { Data = data; }
}
Then, in your UI, create a SerialThread somewhere (perhaps the first time Start is pressed), and have a flag for whether to put things in the text box:
bool reading = true;
SerialThread thread = null;
void StartClick(object s, EventArgs e){
if(thread == null){
thread = new SerialReaderThread();
thread.DataReceived += ThreadDataReceived;
thread.Start();
}
reading = true;
}
void StopClick(object s, EventArgs e){ reading = false; }
Finally, the event handler for the data being received:
void ThreadDataReceived(object s, EventArgs e){
if(reading) Invoke(new EventHandler<DataEventArgs>(ThreadDataReceivedSync), new object[]{ s, e } );
}
void ThreadDataReceivedSync(object s, EventArgs e){
richTextBox.Text += e.Data + "\n";
}
I think this is a better fit to the problem that the BackgroundWorker, even though this is a bit longer than Lukeer's answer (partly because I coded more completely in here than he did). It keeps the separation of the reading and the display, allowing you to log or send the data elsewhere even if the UI is not reading it (if you want a data layer on/off you can put that in the SerialReaderThread).
Edit: After reading the helpful comments, I have modified the solution to compose the thread not inherit from it, as it was pointed out that you can't do that in .Net. You might also want to expose the thread as a property if you want to do any other operations on it. Also, check out Solution 3, if the source provides an event already then you should just use that. I'm not familiar with the port reading API so I wasn't aware of that possibility, and assumed you'd already looked for such an event and ruled it out.