问题
I know that the question may not make sense, and I'm having a tough time trying to think of a way to explain it, so I will show a snippet of code to help. I'm using Winforms on visual studio express 2010:
private void button1(object sender, EventArgs e)
{
txtOutput.Text += "Auto-collecting variables. This may take several minutes";
string v = foo();
txtOutput.Text += "\n" + v;
string b = bar();
txtOutput.Text += "\n" + b;
txtOutput.SelectionStart = txtOutput.Text.Length;
txtOutput.ScrollToCaret(); //scrolls to the bottom of textbox
}
So basically, when the user clicks button1, I want "Auto-collecting variables..." to be displayed in the textbox, and then have foo() execute, display that, and then have bar() execute, and then display that.
What is currently happening is that foo() and bar() execute, and then everything is displayed all at once after foo() and bar() have executed (functions that take several minutes). Is there anyway to fix this, or is there a work around?
Edit: Version of C# is 4.0. If I update to 4.5 or 5.0, will computers without .NET 4.5/5.0 be able to run the .exe?
回答1:
C# 5.0 makes doing this trivial.
Execute the long running tasks in a background thread using Task.Run
and use await
to execute the rest of the method as a continuation in the UI thread without blocking the UI thread for the duration of the asynchronous task.
private async void button1(object sender, EventArgs e)
{
txtOutput.Text += "Auto-collecting variables. This may take several minutes";
string v = await Task.Run(() => foo());
txtOutput.Text += "\n" + v;
string b = await Task.Run(() => bar());
txtOutput.Text += "\n" + b;
txtOutput.SelectionStart = txtOutput.Text.Length;
txtOutput.ScrollToCaret(); //scrolls to the bottom of textbox
}
You can do the same in C# 4.0 like so: (The first solution will be transformed by the compiler into something similar.)
private void button1(object sender, EventArgs e)
{
txtOutput.Text += "Auto-collecting variables. This may take several minutes";
Task.Factory.StartNew(() => foo())
.ContinueWith(t => txtOutput.Text += "\n" + t.Result
, TaskScheduler.FromCurrentSynchronizationContext())
.ContinueWith(t => bar())
.ContinueWith(t =>
{
txtOutput.Text += "\n" + t.Result;
txtOutput.SelectionStart = txtOutput.Text.Length;
txtOutput.ScrollToCaret(); //scrolls to the bottom of textbox
}
, TaskScheduler.FromCurrentSynchronizationContext());
}
回答2:
Use the BackgroundWorker
class to do your processing without blocking UI updates. It has events that can be used to transfer progress information to the UI thread.
回答3:
Depending on the version of .NET, you can use BackgroundWorker (Pre 4.0) or Tasks (Post 4.0 - 3.5 with an add-on)...to name a few.
Backgroundworker Pseudocode:
var backgroundWorker = new BackgroundWorker()
method
{
//Update UI
backgroundWorker.RunWorkAsync()
}
asyncworkmethod
{
//do main logic
}
asynccompletemethod
{
//Update UI to say done
}
Task Pseudocode:
method
{
//Update UI
TaskFactory.StartNew(()=>DoWork).ContinueWith((previousTask)=>UpdateUIToSayDone)
}
And, if you are using 4.5, then you can use the async/await
keyword, however that is just syntactic sugar around tasks (mostly...). Servy already has a decent example of this, though if you go that approach
回答4:
Using a background process (read the other answers) is the correct way to go but if you are looking at a very quick workaround you can call Application.DoEvents() after updating the TextBox. In most cases this call will result in your form updating to reflect the changes you made.
回答5:
txtOutput.Update()
should do what you want, but you should consider using background thread to complete long running task without blocking UI thread.
来源:https://stackoverflow.com/questions/17069489/how-to-update-ui-text-fields-during-long-running-function