2017-01-02 5 views
0

Я создаю форму прогресса, в которой используется фоновая работа для запуска процесса. Она работает нормально в первый раз, отображается форма, но после того, что я получаю ошибкуНе создавать новый экземпляр 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(); 

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

Update 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.

+0

Спасибо, но теперь я получаю еще одно сообщение об ошибке 'Дополнительная информация: Invoke или BeginInvoke не могут быть вызваны на элемент управления до тех пор, пока дескриптор окна не будет создан.'. – dashnick

2

Вы получить эту ошибку, потому что вы подписавшись на это событие:

TheUI.OnProgressUpdate += FPProgUpdate; 

Поэтому FPProgUpdate звонки ReportProgress() несколько раз.

Как вы уже заметили, следующее вроде нет необходимости, и вы можете удалить его:

TheBackgroundworker.ReportProgress(intProg); 
+0

(Как показано ниже) Спасибо, но теперь я получаю еще одно сообщение об ошибке 'Дополнительная информация: Invoke или BeginInvoke не могут быть вызваны на элемент управления до тех пор, пока дескриптор окна не будет создан.'. – dashnick

+0

Попробуйте запустить BackgroundWorker в событии 'Shown' в ProgressForm, а не' Load'. Это кажется слишком ранним. – Marc

+0

Это не помогает, к сожалению ... – dashnick

1

Благодаря Марку за помощь по этому вопросу. Решение было то, что мне нужно было отказаться 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); 
     } 

Захоронение автоматически не отписываться, похоже.