【问题标题】:Not creating new Backgroundworker instance - C#不创建新的 Backgroundworker 实例 - C#
【发布时间】:2017-01-03 07:30:45
【问题描述】:

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

附加信息:此操作已经有 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); 行,我仍然收到此错误:

附加信息:Invoke 或 BeginInvoke 不能在 控制直到窗口句柄被创建。

【问题讨论】:

  • 我认为问题出在其他地方。你在哪里打电话TheBackgroundworker.ReportProgress()?你能展示一下 ProgressForm 吗?

标签: c# winforms


【解决方案1】:

您收到此错误是因为您订阅了此事件:

TheUI.OnProgressUpdate += FPProgUpdate;

因此FPProgUpdate 多次调用ReportProgress()

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

TheBackgroundworker.ReportProgress(intProg);

【讨论】:

  • (如下)谢谢,但现在我收到另一条错误消息Additional information: Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
  • 尝试在 ProgressForm 的 Shown 事件中启动 BackgroundWorker,而不是 Load。这似乎为时过早。
  • 好吧,还有一个问题:TheUI 的实例正在调用 FPProgUpdate。正如斯科特已经提到的那样,这并不好。最好在 BackgroundWorkers ProgressChanged 事件中更新 UI(将代码从 FPProgUpdate 移到那里)。但是在不知道TheUI 的实现的情况下,我们无法告诉您正确的答案...
  • 这就是堆栈溢出帖子针对过程如何报告其进度的解决方案。关于如何做到这一点的任何其他建议?无论如何,我不明白为什么表格没有被处理。同样,它在我第一次运行时仍然有效。感谢您的帮助。
【解决方案2】:

感谢 Marc 提供的帮助。解决方案是我需要从处置方法中的TheUI.OnProgressUpdate 事件中取消订阅FPProgUpdate,我必须重写:

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

处置不会自动退订,好像是这样。

【讨论】:

    【解决方案3】:

    TheBackgroundworker.ReportProgress 只能从正在执行DoWork 的线程内部调用。从您的代码看来,FPProgUpdate 包含一个 ReportProgress,并且是从启动 DoWork 的线程以外的某个线程调用的。

    【讨论】:

    • 谢谢,但现在我收到另一条错误消息Additional information: Invoke or BeginInvoke cannot be called on a control until the window handle has been created.
    猜你喜欢
    • 2022-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-23
    • 2020-05-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多