Reputation: 151
I want to completely stop the BackgroundWorker DoWork() Process while running for closing the form.
I have applied following code but in "this.Invoke" it throws error : "Invoke or BeginInvoke cannot be called on a control until the window handle has been created." while form close.
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
var dt_Images = db.Rings.Select(I => new { I.PaidRs, I.TypeID, I.RingID, I.CodeNo, Image = Image.FromStream(new MemoryStream(I.Image.ToArray())) }).OrderByDescending(r => r.TypeID);
foreach (var dr in dt_Images.ThenByDescending(r => r.RingID).ToList())
{
BTN = new Button();
BTN.TextImageRelation = TextImageRelation.TextAboveImage;
BTN.TextAlign = System.Drawing.ContentAlignment.BottomCenter;
BTN.AutoSize = true;
BTN.Name = dr.RingID.ToString();
BTN.Image = dr.Image;
BTN.Text = dr.CodeNo.ToString() + " " + dr.TypeID.ToString();
this.Invoke(new MethodInvoker(delegate { if (backgroundWorker1 != null) flowLayoutPanel1.Controls.Add(BTN); else return; }));
BTN.Click += new EventHandler(this.pic_Click);
this.Invoke(new MethodInvoker(delegate { if (backgroundWorker1 == null) txt_pcs.Text = flowLayoutPanel1.Controls.Count.ToString(); else return;}));
}
}
private void Form_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.Escape)
{
backgroundWorker1.CancelAsync(); //backgroundworker doesnt stop here
backgroundWorker1 = null; //it still invokes the delegate
this.Dispose();
}
}
How to solved this error?
please help me.
Upvotes: 2
Views: 6814
Reputation: 27673
Here's a simple program which, I think, will give you an idea of how take care of a closing of a form while a BackgroundWorker
is running:
int i = 0;//This is to show progress.
bool cancel;//This is to notify RunWorkerCompleted to Close() the Form if needed.
public Form1()
{
InitializeComponent();
FormClosing += Form1_FormClosing;
backgroundWorker1.WorkerSupportsCancellation = true;
backgroundWorker1.RunWorkerCompleted += backgroundWorker1_RunWorkerCompleted;
backgroundWorker1.RunWorkerAsync();
}
void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
//Since this is executed on the main thread - it is not (as far as I know) going to "race" against the FormClosing.
if (cancel) Close();
else Text = "Done";
}
void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
if (backgroundWorker1.IsBusy)
{
cancel = true;
backgroundWorker1.CancelAsync();
e.Cancel = true;
}
}
private void button1_Click(object sender, EventArgs e)
{
backgroundWorker1.CancelAsync();
}
//This is executed on a separate thread:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
while (!backgroundWorker1.CancellationPending)
{
Invoke((Action)(() => Text = (i++).ToString()));
Thread.Sleep(1000);
}
e.Cancel = true;
}
Upvotes: 1
Reputation: 51224
The actual problem is that there is no way to safely check if form is being closed before calling Invoke
in a background thread.
To remedy this, what you can do is to postpone closing a bit, until the background thread has the chance to exit the main loop.
First, declare two flags and a lock object:
private volatile bool _closeRequest = false;
private volatile bool _workerStopped = false;
private readonly object _lock = new object();
Then, when you want to close the form, simply call Close
:
private void Form_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyData == Keys.Escape)
{
Close();
}
}
Inside FormClosing
, check if worker has already stopped. This part must be inside a monitor, to prevent a race-condition when background thread is just being finished (i.e. to ensure that _workerStopped
and _closeRequest
are updated atomically):
protected override void OnFormClosing(FormClosingEventArgs e)
{
lock (_lock)
{
// if not stopped
if (!_workerStopped)
{
// delay closing
e.Cancel = true;
// notify worker
_closeRequest = true;
}
}
base.OnFormClosing(e);
}
Finally, in your background thread, actually close the form if needed:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
try
{
foreach (var dr in GetImages())
{
if (_closeRequest)
break;
// ... do stuff
}
}
finally
{
lock (_lock)
{
// notify we stopped
_workerStopped = true;
// if closing was postponed, close now
if (_closeRequest)
BeginInvoke(new MethodInvoker(Close));
}
}
}
Upvotes: 1
Reputation: 653
You have to watch for the cancel request inside your worker. Like
private void DoWork(object sender, DoWorkEventArgs e)
{
var worker = sender as BackgroundWorker;
while (!worker.CancellationPending)
{
…
}
if (worker.CancellationPending)
{
e.Cancel = true;
}
}
Upvotes: 2
Reputation: 116857
Inside your DoWork method you have to check against backgroundWorker1.CancellationPending
before starting to process the next image.
You have to waiting until the last image is processed before allowing the user to close the form.
For a comlete example of how to use CancellationPending
and Cancel
properties see this MSDN example
Upvotes: 0