Why does the BackgroundWorker in WPF need Thread.Sleep to update UI controls?
namespace WpfApplication1
{
/// <summary>
/// Interaction logic for Window1.xaml
/// </summary>
pu开发者_StackOverflow中文版blic partial class Window1 : Window
{
BackgroundWorker bgWorker;
Action<int> myProgressReporter;
public Window1()
{
InitializeComponent();
bgWorker = new BackgroundWorker();
bgWorker.DoWork += bgWorker_Task;
bgWorker.RunWorkerCompleted += myWorker_RunWorkerCompleted;
// hook event to method
bgWorker.ProgressChanged += bgWorker_ReportProgress;
// hook the delegate to the method
myProgressReporter = updateProgress;
bgWorker.WorkerReportsProgress = true;
}
private void myWorker_RunWorkerCompleted(object sender, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
object result;
result = e.Result;
MessageBox.Show(result.ToString());
progressBar1.Value = 0;
button1.IsEnabled = true;
}
private void bgWorker_ReportProgress(object sender, ProgressChangedEventArgs e)
{
System.Windows.Threading.Dispatcher disp = button1.Dispatcher;
disp.BeginInvoke(myProgressReporter,e.ProgressPercentage);
//Dispatcher.BeginInvoke(myProgressReporter, DispatcherPriority.Normal, e.ProgressPercentage);
}
private void updateProgress(int progressPercentage)
{
progressBar1.Value = progressPercentage;
}
private void bgWorker_Task(Object sender, DoWorkEventArgs e)
{
int total = 1000;
for (int i = 1; i <= total; i++)
{
if (bgWorker.WorkerReportsProgress)
{
int p = (int)(((float)i / (float)total) * 100);
bgWorker.ReportProgress(p);
}
Thread.Sleep(1); // Without Thread.Sleep(x) the main thread freezes or gives stackoverflow exception,
}
e.Result = "Completed";
}
private void button1_Click(object sender, RoutedEventArgs e)
{
if(!bgWorker.IsBusy)
bgWorker.RunWorkerAsync("This is a background process");
button1.IsEnabled = false;
}
}
}
Because in your (artificial) scenario you pump 1000 request-for-update to the main thread.
It gets no time to do an idle loop (necessary to do a screen update).
But (thanks to TerrorAustralis), you should start with merging your bgWorker_ReportProgress and myProgressReporter methods. You are now synchronizing twice, a possible cause of stackoverflow. Dispatching the UpdateProgress events is one of the main features of the Backgroundworker:
private void bgWorker_ReportProgress(object sender, ProgressChangedEventArgs e)
{
//System.Windows.Threading.Dispatcher disp = button1.Dispatcher;
//disp.BeginInvoke(myProgressReporter,e.ProgressPercentage);
progressBar1.Value = progressPercentage; // safe because we're on the main Thread here
}
Posibility:
Dispatcher.BeginInvoke() is an Asynchronous operation. Since this is the case, you are able to try to hit it again before it completes its operation. To see if this is the problem, try Dispatcher.Invoke() which is Synchronous
As a possible workaround, if you just want to update the progress bar, the backgroundWorker ProgressChanged event could do this without the use of an explicit dispatcher.
精彩评论