简体   繁体   中英

Not creating new Backgroundworker instance - C#

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.

You retrieve this error because you are subscribing to this event:

TheUI.OnProgressUpdate += FPProgUpdate;

Therefore FPProgUpdate calls ReportProgress() multiple times.

As you have already noticed, the following like is not necessary and you can remove it:

TheBackgroundworker.ReportProgress(intProg);

Thanks to Marc for the help on this. The solution was that I needed to unsubscribe FPProgUpdate from the TheUI.OnProgressUpdate event in the disposal method, which I had to override:

protected override void Dispose(bool disposing)
        {
            if (disposed)
                return;

            if (disposing)
            {
                if (components != null)
                {
                    components.Dispose();
                }

                // Dispose stuff here
                TheUI.OnProgressUpdate -= FPProgUpdate;

            }

            disposed = true;
            base.Dispose(disposing);
        }

The disposal does not automatically unsubscribe, it seems like.

TheBackgroundworker.ReportProgress should only be called from inside of the thread that is executing DoWork . From your code it looks like FPProgUpdate contains a ReportProgress and is being called from some thread other than the thread that started DoWork .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM