不创建新的Backgroundworker实例 - C#

时间:2017-01-02 21:52:33

标签: c# winforms

我正在创建一个使用后台工作程序来运行该进程的进度表单。它在第一次显示表单时运行正常,但之后我收到错误

  

附加信息:此操作已经有OperationCompleted   呼吁它,进一步的电话是非法的。

当我尝试调用TheBackgroundworker.ReportProgress()方法时。

我很困惑,因为我在using块中创建了这样的进度表:

using (ProgressForm FPProgForm = new ProgressForm(TheUI))
{
    FPProgForm.ShowDialog();    

    if (FPProgForm.DialogResult == DialogResult.OK)
    {
        // display results screen
    }
}

FPProgForm构造函数中,我正在创建一个新的BackgroundWorker()

TheBackgroundworker = new BackgroundWorker();

因此,每次创建新对话框时,BackGroundWorker都应该是全新的。

更新:根据要求,以下是整个进度表类:

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
        }
    }
}

更新2:即使删除有问题的TheBackgroundworker.ReportProgress(intProg);行,我仍然会收到此错误:

  

附加信息:无法在a上调用Invoke或BeginInvoke   控制,直到创建窗口句柄。

3 个答案:

答案 0 :(得分:2)

您正在订阅此事件时检索此错误:

TheUI.OnProgressUpdate += FPProgUpdate;

因此FPProgUpdate多次调用ReportProgress()

正如您已经注意到的那样,以下内容不是必需的,您可以将其删除:

TheBackgroundworker.ReportProgress(intProg);

答案 1 :(得分:1)

感谢Marc的帮助。解决方案是我需要从处理方法中的FPProgUpdate事件取消订阅TheUI.OnProgressUpdate,我必须覆盖它:

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

处理不会自动取消订阅,似乎就是这样。

答案 2 :(得分:0)

只应从正在执行TheBackgroundworker.ReportProgress的线程内部调用

DoWork。从您的代码看起来FPProgUpdate包含ReportProgress,并且是从除DoWork开始的线程之外的某个线程调用的。