I am creating a progress form that utilizes a backgroundworker to run the process. It runs okay the first time the form is displayed, but after that I get the error
Additional information: This operation has already had OperationCompleted called on it and further calls are illegal.
when I try to call the TheBackgroundworker.ReportProgress()
method.
I am confused, because I am creating the progress form in a using
block like this:
using (ProgressForm FPProgForm = new ProgressForm(TheUI))
{
FPProgForm.ShowDialog();
if (FPProgForm.DialogResult == DialogResult.OK)
{
// display results screen
}
}
And in the FPProgForm
constructor, I am creating a new BackgroundWorker()
TheBackgroundworker = new BackgroundWorker();
So the BackGroundWorker
should be brand new every time I create a new dialog.
Update: On request, here is the entire progress form class:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace FPDWF
{
public partial class ProgressForm : Form
{
public delegate void RunFunctionDelegate();
RunFunctionDelegate FuncToRun { get; } // function to be run
FPDesktopWFUI TheUI { get; }
BackgroundWorker TheBackgroundworker; // for internal use only, like a viagra demo
public ProgressForm(RunFunctionDelegate funcToRun, FPDesktopWFUI theUI)
{
InitializeComponent();
FuncToRun = funcToRun;
TheUI = theUI;
TheBackgroundworker = new BackgroundWorker();
InitializeBackgroundWorker();
// subscription to event stuff here: http://stackoverflow.com/questions/14871238/report-progress-backgroundworker-from-different-class-c-sharp
TheUI.OnProgressUpdate += FPProgUpdate;
}
// Set up the BackgroundWorker object by
// attaching event handlers.
private void InitializeBackgroundWorker()
{
// background worker stuff here: https://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
TheBackgroundworker.DoWork +=
new DoWorkEventHandler(TheBackgroundworker_DoWork);
TheBackgroundworker.RunWorkerCompleted +=
new RunWorkerCompletedEventHandler(TheBackgroundworker_RunWorkerCompleted);
TheBackgroundworker.ProgressChanged +=
new ProgressChangedEventHandler(TheBackgroundworker_ProgressChanged);
TheBackgroundworker.WorkerReportsProgress = true;
TheBackgroundworker.WorkerSupportsCancellation = true;
}
private void ProgressForm_Load(object sender, EventArgs e)
{
// progress bar stuff here: http://stackoverflow.com/questions/12126889/how-to-use-winforms-progress-bar
ui_progbar.Maximum = 100;
ui_progbar.Step = 1;
ui_progbar.Value = 0;
TheBackgroundworker.RunWorkerAsync();
}
private void ui_cancelbutton_Click(object sender, EventArgs e)
{
if (TheBackgroundworker.WorkerSupportsCancellation == true)
{
// Cancel the asynchronous operation.
TheBackgroundworker.CancelAsync(); // there really is no purpose to this as i can just set the contRunning flag I think
TheUI.contRunning = false; // i think this thread safe due to 'volatile flag', https://msdn.microsoft.com/en-us/library/7a2f3ay4(v=vs.100).aspx
resultLabel.Text = "Cancelling...";
}
}
// This event handler is where the time-consuming work is done.
private void TheBackgroundworker_DoWork(object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
FuncToRun();
}
// This event handler updates the progress.
private void TheBackgroundworker_ProgressChanged(object sender, ProgressChangedEventArgs e)
{
// something to do here?
}
// This event handler deals with the results of the background operation.
private void TheBackgroundworker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (TheBackgroundworker.CancellationPending == true) // if (e.Cancelled == true)
{
this.DialogResult = DialogResult.Cancel;
this.Close();
}
else if (e.Error != null)
{
this.DialogResult = DialogResult.Abort;
resultLabel.Text = "Error: " + e.Error.Message;
ui_viewres_btn.Text = "Close";
ui_viewres_btn.Enabled = true;
}
else
{
this.DialogResult = DialogResult.OK;
ui_viewres_btn.Enabled = true;
}
}
private void FPProgUpdate(string progText, double prog)
{
// utilizing this: http://stackoverflow.com/a/14871753/3661120
int intProg = Convert.ToInt32(prog * 100);
if (!TheBackgroundworker.CancellationPending)
{
TheBackgroundworker.ReportProgress(intProg); // doesn't really do anything at this point, but whatev
base.Invoke((Action)delegate
{
resultLabel.Text = progText;
ui_progbar.Value = intProg;
});
}
}
private void ui_viewres_btn_Click(object sender, EventArgs e)
{
this.Close(); // closes the window
}
}
}
Update 2: even when I remove the offending TheBackgroundworker.ReportProgress(intProg);
line, I am still getting this error:
Additional information: Invoke or BeginInvoke cannot be called on a control until the window handle has been created.