Click here to Skip to main content
15,885,546 members
Please Sign up or sign in to vote.
2.50/5 (2 votes)
I have made a windows form in c# . It contains two button one read and another stop .
When i click on the read data it should display data in richtextbox continously when i click on stop . It should stop displaying data in richtextbox.

This is the code i written for read button

<pre lang="cs">mySerialPort->BaudRate = 9600;
mySerialPort->Parity = Parity::None;
mySerialPort->StopBits = StopBits::One;
mySerialPort->DataBits = 8;
mySerialPort->Handshake = Handshake::None;
mySerialPort->Open();
int i=0;
while(i<10)
{
    read = read+mySerialPort->ReadLine();
    i++;
}



This is the code i written for stop button
mySerialPort->Close();


Problem is it displays data twenty times and stops doesn't stop when i click on stop button after i receive one data . My friend advised me to use threading . Can someone help me how to use threading and stuff . A basic tutorial will help . I'm a C# beginner making rapid progress
Posted
Updated 22-Jul-11 7:48am
v3
Comments
Sergey Alexandrovich Kryukov 20-Jul-11 3:14am    
Where is C#?!
--SA

I may very well be wrong, but your code looks a lot more like C++ than C#.

As for your question, WindowsForms provides a class BackgroundWorker in the System.ComponentModel namespace. It's a very convenient way of using a separate thread. MSDN[^] gives an example on how to use it.

To put it simple, you have to
1. instantiate a BackgroundWorker object
2. attach a method to its DoWork event. This method is the one running in a separate thread. Start it by calling RunWorkerAsync() on your BackgroundWorker object.

Additionally, you can attach a method to your BackgroundWorker's RunWorkerCompleted event. This one runs in your main thread again. And it does so automatically after your DoWork method has returned.

Furthermore, you can attach a method to your BackgroundWorker's ReportProgress event. Just as RunWorkerCompleted, it will run in main thread. You can start it by calling ReportProgress() and support an object that can contain anything. Remember to set your BackgroundWorker to ReportsProgress = true.

Oh, and if you don't want to use ReportProgress() but update some control from within DoWork(), you cannot do so directly. You will have to delegate the update to the main thread by something like
private void delegate SetLabelDelegate( Label label, string text);
private void SetLabelText( Label label, string text)
{
    if( label.InvokeRequired)
    {
        label.Invoke(
            new SetLabelDelegate( SetLabelText),
            new object[]{ label, text }
        );
        return;
    }

    label.Text = text;
}
 
Share this answer
 
Comments
BobJanova 19-Jul-11 7:42am    
I will give you a 5 but I think that this case is actually better suited to a single persistent thread for the serial port rather than BackgroundWorker, which I consider to be best suited for one time asynchronous tasks which are run in the thread pool and of which there could potentially be many.
steven8Gerrard 19-Jul-11 8:04am    
Yep c++ . Working on both c++ and c# but posted the c++ code here
In addition to the solutions already provided, depending on exactly what you are trying to do, you might consider using the SerialPort's DataReceived event instead of polling it with ReadLine. Add a handler for DataReceived and do your read in the handler when you know there will be data to be read.

Check out http://msdn.microsoft.com/en-us/library/system.io.ports.serialport.datareceived.aspx[^] for details.
 
Share this answer
 
Comments
steven8Gerrard 21-Jul-11 6:13am    
I tried this method But giving me errors . Can you tell me what is wrong ??

Added this for read button click

sprt = new SerialPort("COM3");
sprt.BaudRate = 9600;
sprt.Parity = Parity.None;
sprt.StopBits = StopBits.One;
sprt.DataBits = 8;
sprt.Handshake = Handshake.None;

sprt.DataReceived+=new SerialDataReceivedEventHandler(sprt_DataReceived);
sprt.Open();

Then created thisdatareceivehandler

private void sprt_DataReceived(
object sender,
SerialDataReceivedEventArgs e)
{


indata= sprt.ReadExisting();
richTextBox1.AppendText(indata);


//Console.Write(indata);

}

Getting errors : Control 'richTextBox1' accessed from a thread other than the thread it was created on.

I dont think i have created any thread . I'm a beginner so can you say why it is showing errors
lukeer 21-Jul-11 9:29am    
The thread crossing issue is covered by the code sample in my solution. Just change Label to RichTextBox and substitute
label.Text = text; with
textBox.AppendText(text);
sid63 15-Nov-13 12:51pm    
u can keep it in another function which looks like
private void function(object sender, EventArgs e){}

and call it as this.Invoke(new EventHandler(function));
Wjousts 21-Jul-11 8:44am    
You didn't create a thread, but the SerialPort object did. The event handler is going to be called on a different thread to the GUI thread so you need to use Control.Invoke to do any GUI updating. If you look at BobJanova's solution above, he has the same problem and the same solution. Look at the code in his ThreadDataReceived handler above. You should be able to adapt it to fit.
steven8Gerrard 22-Jul-11 7:44am    
@lukerr
Still errors Invalid token 'delegate' in class, struct,

Cant get a grip of delegates . This delegates concept is confusing :(
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(); }

 // note: this event is fired in the background thread
 public event EventHandler<DataEventArgs> DataReceived;
 
 private bool closed = false;
 public void Close() { closed = true; }

 private void RunMethod(){
  // I'll just believe that this is correct
  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){
 // Note: this method is called in the thread context, thus we must
 // use Invoke to talk to UI controls. So invoke a method on our
 // thread.
 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.
 
Share this answer
 
v3
Comments
lukeer 20-Jul-11 6:14am    
Very neat. My 5.

In particular, I like the constructor passing a method to its base class that is defined within the child class. I would never have even thought that this could work. And now, reading this, I can't think of any reason why it should not. With the exception that RunMethod would have to be static.

And you cannot inherit from Thread because it's sealed. But in the case at hand, including it in SerialReaderThread instead of inheriting should be possible.
BobJanova 20-Jul-11 17:22pm    
Oops, too much Java thinking. (This is essentially the Java pattern, inherit from Thread and implement Run.) I forgot that it is sealed in .Net. I'll update the solution to compose not inherit as you mention.

RunMethod doesn't have to be static, it will be used in the constructor just the same so it will still be an instance method.

I actually used something similar to this for a TCP socket before I learnt about asynchronous sockets. Perhaps there is a parallel for that that should actually be used here.
Wjousts 20-Jul-11 9:30am    
All very nice, but it looks like you are simply recreating the SerialPort's DataReceived event. Which actually begs the question, why doesn't the OP just use that in the first place?
BobJanova 20-Jul-11 17:20pm    
I didn't know that existed, heh. Yes, I suspect that will solve the OP's problem then.
Member 10446893 25-Apr-14 10:34am    
I Want to Display updated data from the same serial port to Different Four window forms at same time .How can I display this at same time to all these four forms?

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900