简体   繁体   中英

How can I use background worker(or any other solution) to stop freezing my GUI?

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!

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 Task s 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.

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!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM