BackgroundWorker - C# - c#

I am developing a multithreading application using BackroundWorker. In the Do_Work method I call another method, in that method I add a lot of data into a list using a while-statement. My goal is to add all the data that in the list to show in a GridView. How can I do that so every time data adds to the list, the gridview uppdates? Instead of waiting until that the while-statement has finished running. When the while-statment adds a value to the list, the value inserts into the gridview?
It must be in the ProgressChanged, but I dont know how to do that.

Add a progress changed event handler to your worker
In your Do_Work method
BackgroundWorker worker = sender as BackgroundWorker;
worker.ReportProgress(0, new DataObject())
In your progess handler
DataObject data (DataObject)e.UserState;
yourList.Add(data);
If you know how far you are along you can send the actual done count in ReportProgess instead of 0.

My method was to create a class that would hold any data that I am passing back and forth. When you call ReportProgress it will require the percentage increment and an object argument. You put your class into this object argument, and from the ProgressChanged event this object is made available through the ProgressChangedEventArgs. You can then read this data, input it into the control you are wanting to update and then call the Refresh() method on that control to update it in the UI without freezing the interface.
http://msdn.microsoft.com/en-us/library/a3zbdb1t.aspx
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.progresschanged.aspx
Edit: (psuedocode, untested)
private List<Customer> _customerList = new List<Customer>();
protected void DoWork(object sender, DoWorkEventArgs e)
{
MyDataGridView.DataSource = _customerList; // Here is where you'll set your data source and bindings
Load_Customer_Method();
}
private void Load_Customer_Method()
{
int totalCustomers = 20;
int currentCustomer = 1;
for(currentCustomer = 1; currentCustomer <= totalCustomers; currentCustomer++)
{
Customer c = new Customer();
// Configure customer
ReportProgress(100*currentCustomer/totalCustomers, c);
}
}
private void ProgressChanged(object sender, ProgressChangedEventArgs e)
{
Customer addedCustomer = (Customer)e.UserState;
_customerList.Add(addedCustomer); // If bindings are set, this update is automatic
MyDataGridView.Refresh(); // Force a refresh of the screen for this grid to make
// it appear as if the grid is populating one object at
// a time.
}

This might help. I have a class called WorkerThread that does the work we want
static void Main( string[] args )
{
// create the background worker object
BackgroundWorker _worker = new BackgroundWorker();
// tell the background worker it can be cancelled and report progress
_worker.WorkerReportsProgress = true;
_worker.WorkerSupportsCancellation = true;
// a worker thread object where the actual work happens
WorkerThread thread = new WorkerThread();
// add our event handlers
_worker.RunWorkerCompleted += new RunWorkerCompletedEventHandler( thread.RunWorkerCompleted );
_worker.ProgressChanged += new ProgressChangedEventHandler( thread.ProgressChanged );
_worker.DoWork += new DoWorkEventHandler( thread.DoWork );
// start the worker thread
_worker.RunWorkerAsync();
// wait for it to be completed
while( !_worker.CancellationPending )
{
// sleep for a second
Thread.Sleep( 1000 );
}
Console.ReadKey();
}
Now in the WorkerThread class
public class WorkerThread
{
public void DoWork( object sender, DoWorkEventArgs e )
{
//get a handle on the worker that started this request
BackgroundWorker workerSender = sender as BackgroundWorker;
// loop through 10 times and report the progress back to the sending object
for( int i = 0; i < 10; i++ )
{
// tell the worker that we want to report progress being made
workerSender.ReportProgress( i );
Thread.Sleep( 100 );
}
// cancel the thread and send back that we cancelled
workerSender.CancelAsync();
e.Cancel = true;
}
public void RunWorkerCompleted( object sender, RunWorkerCompletedEventArgs e )
{
Console.WriteLine( "Worker Done!!" );
}
public void ProgressChanged( object sender, ProgressChangedEventArgs e )
{
// print out the percent changed
Console.WriteLine( e.ProgressPercentage );
}
}
I am using a custom class, you could use a method in your class that is creating the background worker. Just modify the code in the ProgressChanged event.

I suggest you to read on the backgroundworker. You have all the info and example there :
http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
All you will need to do is to pass your item as parameter in ReportProgress and in the event ProgressChanged add your item in your grid.

Related

Backgroundworker blocks UI

I try to perform an easy task in an other backgroundthread, so the UI doesn't get blocked, but it still gets blocked. Did I forget anything?
public partial class backgroundWorkerForm : Form
{
public backgroundWorkerForm()
{
InitializeComponent();
}
private void doWorkButton_Click(object sender, EventArgs e)
{
if (backgroundWorker.IsBusy != true)
{
// Start the asynchronous operation.
backgroundWorker.RunWorkerAsync();
}
}
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
//BackgroundWorker worker = sender as BackgroundWorker;
if (textBoxOutput.InvokeRequired)
{
textBoxOutput.Invoke(new MethodInvoker(delegate
{
for (int i = 0; i < 10000; i++)
{
textBoxOutput.AppendText(i + Environment.NewLine);
}
}));
}
}
}
While the textBox gets filled, the UI is blocked:
Your app wants to repeatedly send updates from the background thread to the UI. There is a built-in mechanism for this: the ProgressChanged event for the background worker. A ReportProgress call is triggered in the background, but executes on the UI thread.
I do change one thing, however. Performance can degrade with too many cross-thread calls. So instead of sending an update every iteration, I instead will batch them into 100.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
const int maxIterations = 10000;
var progressLimit = 100;
var staging = new List<int>();
for (int i = 0; i < maxIterations; i++)
{
staging.Add(i);
if (staging.Count % progressLimit == 0)
{
// Only send a COPY of the staging list because we
// may continue to modify staging inside this loop.
// There are many ways to do this. Below is just one way.
backgroundWorker1.ReportProgress(staging.Count, staging.ToArray());
staging.Clear();
}
}
// Flush last bit in staging.
if (staging.Count > 0)
{
// We are done with staging here so we can pass it as is.
backgroundWorker1.ReportProgress(staging.Count, staging);
}
}
// The ProgressChanged event is triggered in the background thread
// but actually executes in the UI thread.
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
if (e.ProgressPercentage == 0) return;
// We don't care if an array or a list was passed.
var updatedIndices = e.UserState as IEnumerable<int>;
var sb = new StringBuilder();
foreach (var index in updatedIndices)
{
sb.Append(index.ToString() + Environment.NewLine);
}
textBoxOutput.Text += sb.ToString();
}
EDIT:
This requires you set the background worker's WorkerReportsProgress property to true.
It's not important that you pass a count with the ReportProgress call. I do so just to have something and to quickly check if I can return.
One really should keep in mind about how many events are being invoked and queued up. Your original app had 10,000 cross thread invocations and 10,000 changed text events for textBoxOutput. My example uses 100 cross thread calls since I use a page size of 100. I could still have generated 10,000 changed text events for the textbox, but instead use a StringBuilder object to hold a full page of changes and then update the textbox once for that page. That way the textbox only has 100 update events.
EDIT 2
Whether or not your app needs paging is not the main deal. The biggest take away should be that the background worker really should use ReportProgress when trying to communicate info back to the UI. See this MSDN Link. Of particular note is this:
You must be careful not to manipulate any user-interface objects in
your DoWork event handler. Instead, communicate to the user interface
through the ProgressChanged and RunWorkerCompleted events.
Your invocation code should be outside the loop. Everything in the invoked codeblock, will be executed on the UI thread, thus blocking it.
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
//BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 0; i < 10000; i++)
{
// do long-running task
//if (textBoxOutput.InvokeRequired)
//{
textBoxOutput.Invoke(new MethodInvoker(delegate
{
textBoxOutput.AppendText(i + Environment.NewLine);
}));
//}
}
}
an easier way would be to do completely create your output text, and then paste the full output into the TextBox, then you only need one invocation
protected delegate void SetTextDelegate(TextBox tb, string Text);
protected void SetText(TextBox tb, string Text)
{
if (tb.InvokeRequired) {
tb.Invoke(new SetTextDelegate(SetText), tb, Text);
return;
}
tb.Text = Text;
}
and then inside your dowork
private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
{
StringBuilder sb = new StringBuilder();
//BackgroundWorker worker = sender as BackgroundWorker;
for (int i = 0; i < 10000; i++)
{
sb.AppendLine(i.ToString());
}
SetText(textBoxOutput, sb.ToString());
}

Updating the GUI from background worker

The name of the question is: "Updating the GUI from background worker", but the correct name world be: "Updating the GUI from background worker OR reporting multiple-variables (other than an integer) from background worker"
Please let me explain my situation. In a program I have a background worker which analyses the information.As the result of this analysis - form GUI elements should be populated with necessary data. In GUI I would like to update
2 datagridviews
1 listbox
5 labels
As I understand - I can only natively report 1 int value via ReportProgress() method of background worker.
So the question is - how can I pass a List<> ( + some other variables: string, int) via ReportProgress()? Basically - i want to update the GUI with the information but "1 integer" just won't do.. So either it should be possible to pass multiple variables via an ReportProgress() OR i can use an Invoke from inside the BackgroundWorker itself to update the GUI.. Personally I don't like the Invoke approach... What's your opinion?
Here is my code (see the comments):
private void button9_Click(object sender, EventArgs e) // start BW
{
bw.DoWork += new DoWorkEventHandler(backgroundWorker1_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(backgroundWorker1_RunWorkerCompleted);
bw.ProgressChanged += new ProgressChangedEventHandler(backgroundWorker1_ProgressChanged);
bw.WorkerReportsProgress = true;
bw.WorkerSupportsCancellation = true;
bw.RunWorkerAsync(10);
}
private void button10_Click(object sender, EventArgs e) // cancel BW
{
bw.CancelAsync();
}
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
int count = (int)e.Argument;
for (int i = 1; i <= count; i++)
{
if (bw.CancellationPending)
{
e.Cancel = true;
break;
}
List<List<string>> list_result = new List<List<string>>();
list_result = Proccess();
bw.ReportProgress(list_result.Count()); // right now I can only return a single INT
/////////// UPDATE GUI //////////////
// change datagridview 1 based on "list_result" values
// change datagridview 2
// change listbox
// change label 1
// change label ..
Thread.Sleep(20000);
}
MessageBox.Show("Complete!");
e.Result = sum;
}
private void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
prog_count++;
listBox1.Items.Add("Count: (" + prog_count.ToString() + "/20). Found: " + e.ProgressPercentage.ToString() + ".");
}
There's a UserState parameter when calling ReportProgress.
var list_result = new List<List<string>>();
new backgroundWorker1.ReportProgress(0, list_result);
The parameter type is an object so you'll have to cast it back to the type you need:
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
var userState = (List<List<string>>)e.UserState;
}
The tricky issue with this is, how do you determine whether you're passing back a List, or a list of lists, or a single string, number, etc. You'll have to test for each possibility in the ProgressChanged event.
void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
var myList = e.UserState as List<List<string>>;
if (myList != null)
{
// use list
return;
}
int myNumber;
if (Int32.TryParse(e.UserState.ToString(), out myNumber))
{
// use number
return;
}
var myString = e.UserState.ToString();
// use string
}
Alternatively, you could create a class that holds all the values you need (or use Tuple), run everything in the background to populate that class, then pass that to the RunWorkerCompleted event, and update your UI all at once from there.
I have written two very easy methods that enable you to invoke your code (only if required) and you only need to write your code once. I think this makes Invoke much friendlier to use:
1) BeginInvoke
public static void SafeBeginInvoke(System.Windows.Forms.Control control, System.Action action)
{
if (control.InvokeRequired)
control.BeginInvoke(new System.Windows.Forms.MethodInvoker(() => { action(); }));
else
action();
}
2) Invoke
public static void SafeInvoke(System.Windows.Forms.Control control, System.Action action)
{
if (control.InvokeRequired)
control.Invoke(new System.Windows.Forms.MethodInvoker(() => { action(); }));
else
action();
}
It can be called like this:
SafeInvoke(textbox, () => { textbox.Text = "text got changed"; });
Alternatively you could just
System.Windows.Forms.Form.CheckForIllegalCrossThreadCalls = false;
(which only changes behaviour in debug mode btw) and look if you run into problems.More often than not you actually don't. It took me quite some time to find cases very Invoke is really required for things not to get messed up.
The basic pattern for updating the UI from another thread is:
If controlItem.InvokeRequired Then
controlItem.Invoke(Sub() controlItem.Text = textUpdateValue)
Else
controlItem.Text = textUpdateValue
End If
This could update your list of controls without requiring you to pass anything through ReportProgress. If you would like to update your control from within the thread, I don't believe you need to check InvokeRequired, because it will always be required. However, best practices might be to expose the setting of a control via a property and then to do the full check so you can call it from anywhere.

Background Worker Updating from a different class (preferably via events)

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.
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));
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.
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).

Instantiating a progress bar that runs asynchronously and modifying it with thread safe calls

In my application I have a queue download list which consists of progress bars and the file names. When the user clicks a button the file name and progress bar is instantiated and added to the queue. Files download one at a time and asynchronously. What I want to do is keep all the progress bars of the files that are waiting to be downloaded yellow in color and then turn green when it is being downloaded and then turn blue when they are completed. It currently works if I have CheckForIllegalCrossThreadCalls = false; in the constructor of the custom progress bar. I want to see if there is a way to make thread safe changes to the progress bars.
I have each queue item set up as an object. The queue item objects are created from the main form code (Form1.cs) when a button is pressed and the progress bars are created in the queue item constructor, which is probably where my problem begins. The downloads are started through a function in the queue item object.
Queue Item Snippet
public class QueueItem
{
public bool inProgress;
public QueueBar bar;
public QueueItem(args)
{
bar = new QueueBar();
inProgress = false;
// handle arguments
}
public void Download()
{
// process info
WebClient client = new WebClient();
client.DownloadFileCompleted += new AsyncCompletedEventHandler(client_DownloadFileCompleted);
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(client_DownloadProgressChanged);
client.DownloadFileAsync(url, #savePath);
}
private long lastByte = 0;
private long newByte = 0;
private void client_DownloadProgressChanged(object sender, DownloadProgressChangedEventArgs e)
{
percentValue = e.ProgressPercentage;
bar.Value = e.ProgressPercentage;
newByte = e.BytesReceived;
}
private void client_DownloadFileCompleted(object sender, AsyncCompletedEventArgs e)
{
// change bar color
bar.Value = 100;
}
}
Queue Bar Snippet
public class QueueBar : ProgressBar
{
// variables
public QueueBar()
{
this.SetStyle(ControlStyles.UserPaint, true);
// initialize variables
}
// function to change text properties
// function to change color
protected override void OnPaint(PaintEventArgs e)
{
// painting
}
}
Main Function Snippet
public partial class Form1 : Form
{
private List<QueueItem> qItems;
private BackgroundWorker queue;
private void button_Click(object sender, EventArgs e)
{
// basic gist of it
qItems.Add(new QueueItem(args));
Label tmpLabel = new Label();
tmpLabel.Text = filename;
tmpLabel.Dock = DockStyle.Bottm;
splitContainerQueue.Panel2.Controls.Add(tmpLabel);
splitContainerQueue.Panel2.Controls.Add(qItems[qItems.Count - 1].bar);
if (!queue.IsBusy) { queue.RunWorkerAsync(); }
}
private void queue_DoWork(object sender, DoWorkEventArgs e)
{
while (qItems.Count > 0)
{
if (!qItems[0].inProgress && qItems[0].percentValue == 0)
{
qItems[0].inProgress = true;
qItems[0].Download();
}
// else if statements
}
}
I also just tried creating a background worker to create the Queue Items and add the controls asynchronously but that doesn't work since the split container was created on a different thread.
You cannot call a UI control (created on your UI thread) from another thread safely - you need to use InvokeRequired / BeginInvoke() for such calls. When calling BeginInvoke() you'll pass a delegate; something like this (just some sample code, yours will look slightly different):
private void SomeEventHandler ( object oSender, EventArgs oE )
{
if ( InvokeRequired )
{
MethodInvoker oDelegate = (MethodInvoker) delegate
{
SomeEventHandler ( oSender, oE );
};
BeginInvoke ( oDelegate );
return;
}
else
{
// already on the correct thread; access UI controls here
}
}
You also cannot create your progress bars away from the UI thread - you need to create all your controls as part of your UI and then if you need to access these progress bars from your queue items, you'll have to pass in a reference to the progress bar. When you try to access the progress bar, you'll do
if ( bar.InvokeRequired ) { ... }
to determine if you're trying to call it from the right thread.
The reason for this mess is because controls handle many of their property updates through messages and those messages must be delivered synchronously, in the correct order. The only way to ensure this (without some very complex coding) is to create all controls on the same thread where the thread runs a message pump.

DoWork of BackgroundWorker is called twice when RunWorkerAsync is called once?

I have create a backgroundworker in an class it works, but if i call and wait until the end run, call it for the second time it will do the same process twice
i thinks there is somthing wrong with bw.DoWork +=
private void button1_Click(object sender, EventArgs e)
{
nptest.test.start("null", "null");
}
namespace nptest
{
class test
{
public static void start(string str, string strb)
{
if (bw.IsBusy != true)
{
bw.WorkerSupportsCancellation = true;
bw.DoWork += (obj, e) => bw_DoWork(str, strb);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
bw.RunWorkerAsync();
}
}
private static BackgroundWorker bw = new BackgroundWorker();
private static void bw_DoWork(string str, string strb)
{
System.Windows.Forms.MessageBox.Show("initializing BackgroundWorker");
}
private static void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if ((e.Cancelled == true))
{
Console.WriteLine("Canceled");
}
else if (!(e.Error == null))
{
Console.WriteLine("Error: " + e.Error.Message);
}
bw.Dispose();
}
}
}
problem solved
class test
{
private static List<object> arguments = new List<object>();
// initializing with program startup
public static void bwinitializing()
{
bw.WorkerSupportsCancellation = true;
bw.DoWork += new DoWorkEventHandler(bw_DoWork);
bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted);
}
public static void start(string str, string strb)
{
if (bw.IsBusy != true)
{
arguments.Clear();
arguments.Add(str);
arguments.Add(strb);
bw.RunWorkerAsync(arguments);
}
}
private static BackgroundWorker bw = new BackgroundWorker();
private static void bw_DoWork(object sender, DoWorkEventArgs e)
{
List<object> genericlist = e.Argument as List<object>;
System.Windows.Forms.MessageBox.Show("BackgroundWorker " + genericlist[0]);
}
I would suspect that multiple DoWork events are being inadvertently added.
That is, every time the start method is called it registers a new DoWork event handler. This adds and does not replace the existing handler DoWork handler. So then there will be multiple DoWork handlers called subsequent times .. 1, 2, 3, etc.
// creates a NEW delegate and adds a NEW handler
bw.DoWork += (obj, e) => bw_DoWork(str, strb);
I would recommend not using a closure here, but rather just use a Method Group (with implicit conversion to a delegate) and then pass the data to the RunWorkerAsync call (there is a form that takes an argument for data).
The RunWorkerCompleted += line doesn't have this issue because it is passed a delegate from a Method Group (which is guaranteed to always evaluate to the same delegate object1). Thus the repeated += calls for that line will replace the handler.
Example:
class MyData {
public string StrA { get; set; }
}
// These only need to be setup once (and should be for clarity).
// However it will be "ok" now if they are called multiple times
// as, since the delegates are the same, the += will
// act as a replacement (as it replaces the previous delegate with itself).
bw.WorkerSupportsCancellation = true;
bw.DoWork += bw_DoWork;
bw.RunWorkerCompleted += bw_RunWorkerCompleted;
// Pass data via argument
bw.RunWorkerAsync(new MyData {
StrA = str,
});
void bw_DoWork (object sender, DoWorkEventArgs e) {
var data = (MyData)e.Argument;
var str = data.StrA;
// stuff
}
1 I am not sure if it is guaranteed to be reference-equals equality, but using this approach allows for stable invoking of += and -= from the delegate from the Method Group even if obtained by new DelegateType(MethodGroup).
Wrt. my comment in the main post: if UI elements are accessed from a thread on which they were not created then there will fun "Cross-thread operation exceptions". I believe this usage of a Message Box is "okay" (when not created with an owner from another thread), but the practice of accessing the UI in a BackgroundWorker's DoWork is generally dubious.
Also, do not call bw.Dispose() here; dispose it with the owning container or context. It appears to be nice and benign in this case, but only do it when that BGW instance will never be used again. Calling it from an event handler is also dubious as the BGW is still "active".
I have encounter same problem as above commenter "Power-Mosfet"
and in the end, added a new BackgroundWorker() then assigned to the global bw value will fix my problem.
code is, change from:
private BackgroundWorker gBgwDownload;
private void yourFunction_bw(xxx)
{
// Create a background thread
gBgwDownload.DoWork += bgwDownload_DoWork;
gBgwDownload.RunWorkerCompleted += bgwDownload_RunWorkerCompleted;
//omited some code
gBgwDownload.RunWorkerAsync(paraObj);
}
to:
private BackgroundWorker gBgwDownload;
private void yourFunction_bw(xxx)
{
// Create a background thread
gBgwDownload = new BackgroundWorker(); /* added this line will fix problem */
gBgwDownload.DoWork += bgwDownload_DoWork;
gBgwDownload.RunWorkerCompleted += bgwDownload_RunWorkerCompleted;
//omited some code
gBgwDownload.RunWorkerAsync(paraObj);
}
There is also another reason. look for DoWorkEventHandler in its generated code InitializeComponent() If you have generated it through compnent UI properties and also registering it yourself.
Because if you register it again it will not override the previous one but will add another event and will call twice.
In my case, BackgroundWorker was running twice because in the constructor class of my form I declared the DoWork, ProgressChanged and RunWorkerCompleted event handlers, but it was already declared by Visual Studio 2013 in Designer part of this form class.
So, I just deleted my declarations and it worked fine.
thank you....this code is working fine... creating new intance for backroundworker is good idea....
Now we can call this function in for/while loop and can run multiple backgroundworker process.
I coded like this
when button click is done.. without distrubting the main thread flow... multiple process will be running back side....
i just used messagebox to pop up..but we can do timetaking process to run in "bgwDownload_DoWork" function... and multiple process will be created... and her we need not check the BackgroundWorker is busy or not...
private void button1_Click(object sender, EventArgs e)
{
for (int i = 0; i < 3; i++)
yourFunction_bw(i);
}
private BackgroundWorker gBgwDownload;
private void yourFunction_bw(int i)
{
// Create a background thread
gBgwDownload = new BackgroundWorker(); // added this line will fix problem
gBgwDownload.DoWork += bgwDownload_DoWork;
gBgwDownload.RunWorkerAsync(i);
}
private void bgwDownload_DoWork(object sender, DoWorkEventArgs e)
{
int stre = (int)e.Argument;
MessageBox.Show(stre.ToString ()); // time taken process can be added here
}
I ran into this problem today, I put a background worker on a popup form that was doing a long running task when I noticed that every time I showed the form the background worker RunWorkerCompleted event was being called multiple times.
My problem was that I was not disposing of the form after closing it, which meant every time I showed the form it added another handler to the even each time.
Disposing of the form when finished with it solved my problem. Just wanted to mention it here as I came across this page when I went looking for a solution for my situation.
I removed the control from the designer and instantiate a new WorkerProcess in Code:
example:
var bwProcess = new BackgroundWorker();
bwProcess.DoWork += new DoWorkEventHandler(bwProcess_DoWork);
bwProcess.RunWorkerCompleted += bwProcess_RunWorkerCompleted;

Categories