0

I'm trying to fix a problem in my Winforms application where the GUI tends to lock up.

Current Solution: The application is written to read from a serial port and write to a rich textbox in the GUI. Now, the response can be a single response or continuous streaming at high speed, based on the input .

As of now I'm updating the textbox with content as and when I receive it from the device,ie. with an event handler that triggers when data is received from the serial port.

Is background worker the only solution to the problem? If yes, how do I restructure my solution to accomodate this change?(I understand the background worker cannot access the GUI). If not, are there any better solutions?

EDIT for Code: Here is the general code flow

//Function triggered when data received from serial port
   private void DataReceived(object sender, EventArgs e)
        {
             while (serialPort1.BytesToRead > 0 && serialPort1.IsOpen)
            {
                 //calls to several processing functions
                  //which then call a function writetoTextBox and pass the data to write
           }
        }
//write to textbox
void writeToTextBox(inputdata)
{
// write to textbox. 
//If user has asked to write to file. Open a file dialog to get file name and write to it
// as well. As of now when the data rate is high, this part doesnt get the time to respond
//as the GUI locks up the thread
}

Disclaimer: I'm relatively new to winforms and C# as such. So any advice would be appreciated!

seeker
  • 6,841
  • 24
  • 64
  • 100

2 Answers2

3

You can use several approaches here:

I recommend using the async/await method since MS already did the hard work of making sure everything stays synchronized, but you can decide what you want based on your app.

To make sure that you can access the UI thread, you'll have to use the Invoke method: Updating the GUI from another thread - SO

An example of rolling your own Thread would be the following:

  • Putting all your serial handing code in it's own object/class
  • Instantiate object from the Main From start the receiving method on it's own thread
  • When data is received, raise an event back to the main thread

To avoid locking the UI, you could do something like the following:

private delegate void writeToTextBoxDelegate(List a, List b);

private async void DataReceived(object sender, EventArgs e)
{
    while (serialPort1.BytesToRead > 0 && serialPort1.IsOpen)
    {
        await Task.Factory.StartNew(() =>
        {
            // Do whatever work you want to do here.
            // When you're all done, call the following line.
            textBox.Invoke(
                new writeToTextBoxDelegate(writeToTextBox),
                new object[] { a, b }
            );
        });
    }
}

If the extra work you're doing inside the while loop is not significant, you may want to move the

await Task.Factory.StartNew(() => { });

to outside the while loop. The goal is to not tie up the Task too badly, since the number of threads which are allowed to run Tasks are limited.

An alternate method of calling the Invoke would be the following:

private delegate void writeToTextBoxDelegate(List a, List b);

private void writeToTextBox(List a, List b)
{
    if (textBox.InvokeRequired)
    {
        textBox.Invoke(new writeToTextBoxDelegate(writeToTextBox),
            new object[] { a, b });

        return;
    }

    // Your custom write-to-textbox code here.
}

You could then simply call writeToTextBox from wherever, and it would handle the invoke itself.

Community
  • 1
  • 1
Ming Slogar
  • 2,327
  • 1
  • 19
  • 41
  • Would it be possible for you to show(with trivial examples?) how I could use it for my need?I've added some more details to the question. – seeker Apr 05 '14 at 22:42
  • Thank you for the code snippet. The actual function prototype is `writeToTextBox(List a,List b)` .. so would the `textBox.Invoke` become `textBox.Invoke(new writeToTextBoxDelegate(writeToTextBox), new object[] { List a,List b }`? – seeker Apr 07 '14 at 07:09
  • Also, from your snippet, It seems you call `writeToTextBox` from `DataReceived`. However, in the code, `writeToTextBox` is actually called from other functions (who are in turn called from `DataReceived`). Would this modify your snippet? – seeker Apr 07 '14 at 07:14
0

to avoid recoding all your project, you can try to use Application.DoEvents().

void writeToTextBox(inputdata)
{
    /*your code */
     Application.DoEvents();
}

You can see there the description of the method and a little example. Hope this helps!

Emi987
  • 385
  • 1
  • 12
  • Advice to anyone who might want to use this. Don't. The number of weird errors this creates later, isnt worth it. See here http://stackoverflow.com/questions/5181777/use-of-application-doevents – seeker Apr 12 '14 at 22:18