2017-01-02 76 views
0

我正在創建一個利用backgroundworker運行此進程的進度表單。它運行好第一時間顯示的形式,但在那之後我得到的錯誤不創建新的Backgroundworker實例 - C#

Additional information: This operation has already had OperationCompleted called on it and further calls are illegal.

當我嘗試調用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);線,我仍然得到這個錯誤:

Additional information: Invoke or BeginInvoke cannot be called on a control until the window handle has been created.

+0

我認爲問題在別的地方。你在哪裏調用'TheBackgroundworker.ReportProgress()'?你能展示ProgressForm嗎? – Marc

回答

0

TheBackgroundworker.ReportProgress只應從執行DoWork的線程內部調用。從您的代碼看起來像FPProgUpdate包含一個ReportProgress,並從除了啓動DoWork的線程以外的某個線程調用。

TheUI.OnProgressUpdate += FPProgUpdate; 

因此FPProgUpdate電話ReportProgress()多次:

+0

謝謝,但現在我得到另一個錯誤消息'其他信息:調用或BeginInvoke無法調用控件,直到窗口句柄已被創建.'。 – dashnick

2

你,因爲你訂閱該事件檢索此錯誤。

正如你已經注意到了,下面喜歡的是沒有必要的,可以將其刪除:

TheBackgroundworker.ReportProgress(intProg); 
+0

(如下)謝謝,但現在我收到另一個錯誤消息'其他信息:調用或BeginInvoke不能在控件上調用,直到窗口句柄被創建.'。 – dashnick

+0

嘗試在ProgressForm的'Shown'事件中啓動BackgroundWorker,而不是'Load'。這似乎爲時尚早。 – Marc

+0

這沒有幫助不幸... – dashnick

1

感謝馬克對這種幫助。該解決方案是,我需要從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); 
     } 

處置不會自動取消,它看起來像。