Reputation: 8594
In my application's Business Logic layer I have the following classes:
public class EocMonitor : DeviceMonitor {
public BackgroundWorker BackendWorker { get; set; }
public BackgroundWorker EocWorker { get; set; }
public EocMonitor() {
BackendWorker = new BackgroundWorker {
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
BackendWorker.DoWork += BackendWorker_DoWork;
EocWorker = new BackgroundWorker {
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
EocWorker.DoWork += EocWorker_DoWork;
}
private void BackendWorker_DoWork( object sender, DoWorkEventArgs e ) {
// Does some lengthy operation
}
void EocWorker_DoWork( object sender, DoWorkEventArgs e ) {
// Does some lengthy operation
}
public void GetDiagnostics() {
BackendWorker.RunWorkerAsync( new DiagnosticsInfo() );
EocWorker.RunWorkerAsync( new DiagnosticsInfo() );
}
}
public class DiagnosticsInfo {
public int DataTypeCount { get; set; }
public int DataTypesProcessed { get; set; }
}
The BackgroundWorkers
are used to query information over the wire from 2 other processes running in my application. The responses can take a while to come back. Plus the data can take a while to come back.
I have a WPF UserControl in my application's main window called Dashboard
. The Dashboard
has a DataGrid
on it that displays the results of the lengthy operations. Because they are lengthy, it also has a Button
on it called Refresh that starts the process off. And, because it can take a long time to run, there's a UserControl I wrote called a ProgressControl
on the form. This consists of a Cancel Button,
a ProgressBar
, and a TextBlock
where messages can be displayed. When the user clicks on the Cancel Button
, the refresh stops.
Here's some code from Dashboard
:
public partial class Dashboard : UserControl {
public Dashboard() {
InitializeComponent();
}
private Dashboard_Loaded( object sender, RoutedEventArgs e ) {
if ( !setupProgress && EocMonitor != null ) {
EocMonitor.BackendWorker.ProgressChanged += BackendWorker_ProgressChanged;
EocMonitor.BAckendWorker.RunWorkerCompleted += BackendWorker_RunWorkerCompleted;
EocMonitor.EocWorker.ProgressChkanged += EocWorker_ProgresChanged;
EocMonitor.EocWorker.RunWorkerCompleted += EocWorker_RunWorkerCompleted;
}
}
private void BackendWorker_ProgressChanged( object sender, ProgressChangedEventArgs e ) {
DiagnosticsInfo info = e.UserState as DiagnosticsInfo;
// Other processing to notify the user of the progress
}
private void BackendWorker_RunWorkerCompleted( object sender, RunWorkerCompletedEventArgs e ) {
// Processing to do when the back-ground worker is finished
}
private void DiagnosticsProgressCtrl_Click( object sender, RoutedEventArgs e ) {
EocMonitor.BackendWorker.CancelAsync();
EocMonitor. EocWorker.CancelAsync();
DiagnosticsProgressCtrl.Visibility = Visibility.Collapsed;
e.Handled = true;
}
void EocWorker_RunWorkerCompleted( object sender, RunWorkerCompletedEventArgs e ) {
// Processing to do when the back-ground worker is finished
}
private void RefreshButton_Click( object sender, RoutedEventArgs e ) {
DiagnosticsProgressCtrl.Maximum = DiagnosticsProgressCtrl.Minimum = DiagnosticsProgressCtrl.Value = 0.0;
DiagnosticsProgressCtrl.Visibility = Visibility.Visible;
backendDataTypeCount = eocDataTypeCount = 0;
backendWorkerCompleted = eocWorkerCompleted = false;
EocMonitor.GetDiagnostics();
e.Handled = true;
}
}
The problem is that I have placed breakpoints in the DoWork methods and watched them run to completion, yet the RunWorkerCompleted methods are not being called. No errors are occurring or being thrown. This thing is the EocMonitor class and the Dashboard class are in two different DLLs. Does that make a difference? As far as I know it shouldn't, but I don't understand why the completed event handlers aren't getting called. Should I instantiate the BackgroundWorkers
in the front-end application?
Tony
Upvotes: 0
Views: 1399
Reputation: 8594
Well, after I posted the above, I went back and changed things a bit. I now instantiate the BackgroundWorker
objects in the Dashboard
control and pass them to the EocMonitor's GetDiagnostics
method. The properties in EocMonitor that hold these objects have private setters, so the only way to use them is to create them & pass them to that method. The code in the Dashboard_Loaded
is now moved in the RefreshButton_Click
method and runs after the objects are instantiated, before they're passed to GetDiagnostics.
This all works now! I see the Progress_Changed
methods and the RunWorkerCompleted
methods run.
It just hit me why it's probably not working. The EocMonitor object is created on a non UI thread during my program's initalization phase. Since it's calling methods in a UI object, the methods probably can't be called. An Invalid operation exception of some sort is probably being thrown, but there's no place to catch it.
So let that be a lesson: The BackgroundWorker
has to be instantiated in code on the UI thread.
Upvotes: 0
Reputation: 292425
The event is raised, but you don't see it because you didn't subscribe to the RunWorkerCompleted
event...
BackendWorker.RunWorkerCompleted += BackendWorker_RunWorkerCompleted;
EocWorker.RunWorkerCompleted += EocWorker_RunWorkerCompleted;
Upvotes: 2