问题
I have a background worker in my GUI class.
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
ProgressClass obj = new ProgressClass();
Importer tradeImporter = e.Argument as Importer;
BackgroundWorker worker = sender as BackgroundWorker;
List<TradeUploadInfo> list = obj.AllocateTrades2(tradeImporter, false);
e.Result = list; //Passes the list for processing
}
Importer is my own class. Now, the AllocateTrades2
method has all the processing done in it.
My question is, how would I go about performing a bw.ProgressReport
inside the AllocateTrades2
method, which is in a different class without passing the bw as a parameter?
Would be great if someone explained it to me how to do it with events, but if there is another elegant way. I'm open for ideas.
回答1:
If you don't want to pass in the entire BGW (justifiably so) so as to not expose more than it needs to know, one option is to just pass in a delegate that you assign a ReportProgress
call to.
Adjust the signature of AllocateTrades2
to be:
public List<TradeUploadInfo> AllocateTrades2(
Importer importer, bool flag, Action<int> reportProgress)
Invoke the reportProgress
delegate as appropriate from within that method.
Then adjust the call to AllocateTrades2
like so:
obj.AllocateTrades2(tradeImporter, false,
progress => worker.ReportProgress(progress));
回答2:
Well, given the fact that AllocateTrades2
runs in the context of the background worker, any events that it raises are also executed in that context.
So, all you need to do is add a new event in your ProgressClass
, say NotifyProgress
, and bind it to the class where you have the background worker.
So:
//In class ProgressClass.
public event EventHandler<ProgressClassEventArgs> NotifyProgress = (s, e) => {};
And next:
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
ProgressClass obj = new ProgressClass();
//Here you hook up the event
obj.NotifyProgress += this.OnProgressChanged;
Importer tradeImporter = e.Argument as Importer;
BackgroundWorker worker = sender as BackgroundWorker;
List<TradeUploadInfo> list = obj.AllocateTrades2(tradeImporter, false);
e.Result = list; //Passes the list for processing
}
The event handler would look like this:
private void OnProgressChanged(object sender, ProgressClassEventArgs e)
{
worker.ReportProgress(e.Progress);
}
It's OK, since you can (or you already do) have the worker as a member in this class.
You will need to define the ProgressClassEventArgs
(EventArgs
subclass) and add a Progress
property of type int in this case, to match the ReportProgress
args.
回答3:
If you are able/willing to modify the obj.AllocateTrades2 method, you can yield results and then add each item to your list in a loop.
Example:
public IEnumerable<TradeUploadInfo> AllocateTrades2(Importer tradeImporter, bool foo)
{
foreach( ... )
{
TradeUploadInfo bar; // = ...
// ...
yield return bar;
}
}
private void bw_DoWork(object sender, DoWorkEventArgs e)
{
ProgressClass obj = new ProgressClass();
Importer tradeImporter = e.Argument as Importer;
BackgroundWorker worker = sender as BackgroundWorker;
List<TradeUploadInfo> list = new List<TradeUploadInfo>();
foreach ( TradeUploadInfo info in obj.AllocateTrades2(tradeImporter, false) )
{
list.Add( info );
// ... progress
}
e.Result = list; //Passes the list for processing
}
The beauty here is that you can use AllocateTrades2 exactly as you did before (meaning you don't have to modify existing code or overload the function) (hmm.. actually, you would need to modify code that was explicitly expecting a List, probably by just adding .ToList()
after the function call) and you don't need to add events (which can get a little tricky when it comes to garbage collection).
来源:https://stackoverflow.com/questions/16677774/background-worker-updating-from-a-different-class-preferably-via-events