BackgroundWorker: Enabling/Disabling Buttons in Completed Event not working after switching between UserControls
I have a problem with my BackgroundWorker
placed in a UserControl
.
My WPF application has a navigation on the left and each entry loads its own UserControl
where the user can generate a PDF file.
Because the creation of the PDF takes some time I've implemented a BackgroundWorker
which does the job and additionally I disable some buttons and show a progressbar.
In the RunWorkerCompleted
event I reset the state of the buttons and hide the progressbar.
All of this is working very well, despite one scenario:
While the PDF creation is running the user can switch between the UserControls and if he returns to the control where he startet the job, the control should display the progressbar and the buttons as disabled.
To achieve this I added a variable (isProcessing) to the UserControl
.
A the constructor of the control I have this:
// Check if a current process is running, if so: handle button/progressbar visibility
if (_isProcessing)
{
_stkProgressBar.Visibility = Visibility.Visible;
progressBar1.IsIndeterminate = true;
// Disabling the buttons here is just working with a hack in
// the "Button_IsEnabledChanged" event.
btnDaten.IsEnabled = false;
btnBericht.IsEnabled = false;
this.Cursor = Cursors.Wait;
}
else
{
_stkProgressBar.Visibility = Visibility.Hidden;
}
Enabling/Disabling the buttons there is just working because of this dirty hack:
private void btnDaten_IsEnabledChanged(object sender, DependencyPropertyChangedEventArgs e)
{
//HACK: We want to disable the report buttons if a report execution is running.
// Disabling the buttons in the Load Event of the control isn't working
if (_isProcessing && (bool)e.NewValue)
{
btnDaten.IsEnabled = false;
btnBericht.IsEnabled = false;
}
}
Now if a job is running and the user switches between the control, the state of the processing control is fine. But if the job is over and the PDF ready, the buttons can't get enabled and the progressbar also stays visible. The code is placed in the RunWorkerCompleted
event:
void worker_RunWorkerCompleted(object sender, RunW开发者_如何学运维orkerCompletedEventArgs e)
{
// This is not working if the user switches between the controls
_isProcessing = false;
this.Cursor = Cursors.Arrow;
_stkProgressBar.Visibility = Visibility.Hidden;
btnDaten.IsEnabled = true;
btnBericht.IsEnabled = true;
}
I debugged it and saw that the buttons get the right input and therefore should be enabled, but nothing happens. If the user stays in the control where he startet the job, the state of the buttons and the progressbar is resetted correctly.
Yes, the switching between user controls is the problem here. When you switch away and switch back, you create a new instance of the control. Which creates a new instance of the BackgroundWorker. Which has a RunWorkerCompleted event handler that is not associated with the BGW that is actually running.
There's another bug in your code, this should crash your program with an ObjectDisposedException when the original BGW instance finishes the job and sets the (now invisible) control properties. You are forgetting to call Dispose() on the user controls when you switch between them. Not quite sure how that's done in WPF but in winforms that is an unpluggable leak.
You are going to have to this differently as long as you want to support switching. The BGW instance needs to be separate from the user control instance so it can survive the switch. Fairly painful, you'll have to wire and unwire the events as the control gets created and disposed, you definitely need to override the Dispose() method and not forget to call it. Making the BGW static is defensible if you only allow the job it does to run one-at-a-time. Which should be normal. Stacking the user controls so you only ever create them once is a Q&D fix.
精彩评论