UI still non responsive after using control.begini

2019-05-23 14:48发布

问题:

i've made a C# winforms application. Now i have a form which has lots of buttons, which call huge number crunching functions whose output i update in a textbox. I call the textbox.begininvoke() method to which i pass a delegate to the function which updates the text in the textbox, however when the text is huge, the form is non responsive as i can't click on the cancel button. Isn't there any way so that the whole form remains responsive and as well the update too keeps happening. I have to show the data to the user as it is coming, i can't buffer the whole thing and show in the end. I also tried to implement my own buffer and show data at particular intervals which works great for small amount of text, but in huge amount the UI just doesn't respond. any help? Thanks

updating question as some confusions are arising

  • i've called the number crunching function on a separate thread.
  • that number crunching function calls the control.begininvoke function whenever data arrives to update the textbox
  • MY UI gets to be displayed and i see the output coming, but when the data is huge, i can't do any other activity though i can still see the UI

回答1:

Calling BeginInvoke (or Invoke) will not buy you anything unless the number crunching function is running on another thread than the UI thread.

Consider the following code:

private void HardWork(object state)
{
    for (int i = 0; i < 10; i++)
    {
        Thread.Sleep(500);
        SetText(i.ToString());
    }
}

private void SetText(string text)
{
    if (this.InvokeRequired)
    {
        this.Invoke(new Action<string>(SetText), text);
    }
    else
    {
        textBox1.Text = text;
    }
}
private void Button_Click(object sender, EventArgs e)
{
    ThreadPool.QueueUserWorkItem(HardWork);            
}

The Button_Click method will start executing the method HardWork on a separate thread. HardWork will do some processing (simulated by the Thread.Sleep call) and then call a method to display some progress. Inside this method, we need to check whether we are on the UI thread or not. If we are not, we invoke the same method using Invoke (or BeginInvoke) in order to force it to execute on the UI thread.

Update: if the amount of data emitted from the number crunching method is very large, this might of course have a negative impact on the UI responsiveness. If you for instance accumulate a large amount of text in your threaded method and emit that text on every update, that will be slower than just emitting what has changed since the last update. The same goes for the text box; calling TextBox.AppendText with just the new text will be faster than repedetly assigning the TextBox.Text property.

It's hard to give more detailed ideas on how to solve your particular problem since we have not see what your code actually does.



回答2:

You got it backwards.

BeginInvoke is what you should use to update the UI. It does not spawn a new thread, as you seem to believe.

BeginInvoke is simply "Execute the following within the thread that the control was originally created in", which is your UI thread.

BeginInvoke is therefore what you should use in your number crunching thread to post back updates into the UI.

Hope this helps



回答3:

Have the function that updates the text box do it piece by piece and call DoEvents between each update. That is, break up the string and ...

update piece 1
DoEvents
update piece 2
DoEvents
...
update piece n