2014-12-07 8 views
6

Ich habe eine WinForm mit einem Background:Background: InvalidOperationException in RunWorkerCompleted

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 
using SeoTools.Utils; 

namespace SeoTools.UI 
{ 
    public partial class UIProgress : Form 
    { 
     public UIProgress(DoWorkEventHandler doWorkEventHandler, RunWorkerCompletedEventHandler runWorkerCompletedEventHandler) 
     { 
      InitializeComponent(); 
      this.backgroundWorker.WorkerReportsProgress = true; 
      this.backgroundWorker.WorkerSupportsCancellation = true; 
      this.backgroundWorker.DoWork += doWorkEventHandler; 
      this.backgroundWorker.RunWorkerCompleted += runWorkerCompletedEventHandler; 
     } 

     public void Start() 
     { 
      var foo = SynchronizationContext.Current; 
      backgroundWorker.RunWorkerAsync(); 
     } 

     private void btnStop_Click(object sender, EventArgs e) 
     { 
      btnStop.Enabled = false; 
      btnStop.Text = "Stopping..."; 
      backgroundWorker.CancelAsync(); 
     } 

     private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) 
    { 
     try 
     { 
      wdgProgressBar.Value = e.ProgressPercentage; 
      if (this.Visible == false) 
      { 
       this.ShowDialog(); 
       this.Update(); 
      } 
     } 
     catch (InvalidOperationException) {} 
    } 

     private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
     { 
      this.Hide(); //Here I get a InvalidOperationException 
      this.Dispose(); 
     }  
    } 
} 

erste Mal, dass ich laufen diese es funktioniert gut. Aber das zweite Mal bekomme ich InvalidOperationException beim Aufruf this.Hide().

"Weitere Informationen: Cross-Thread-Operation nicht gültig: Steuern Sie 'UIProgress' von einem anderen Thread als dem Thread, auf dem es erstellt wurde."

Das Seltsame ist auf ersten Lauf foo in Start() ist ein WindowsFormsSyncronizationContext aber auf dem zweiten Versuch, es ist ein System.Threading.SyncronizationContext.

Die Anwendung, die ich schreibe, ist ein ExcelDna-Plugin.

UIProgress uiProgress = new UIProgress(
       delegate(object sender, DoWorkEventArgs args) 
       { 
        .... 
       }, 
       delegate(object sender, RunWorkerCompletedEventArgs args) 
        { 
         ... 
        } 
      ); 
      uiProgress.Start(); 
+0

Wie 'Start' aufgerufen wird? – kennyzx

+0

@kennyzx Ich habe meine Frage –

+1

aktualisiert Ich habe eine alte [Beitrag] (http://blogs.msdn.com/b/mattdotson/archive/2006/02/13/531315.aspx) auf SyncronizationContext, die Technik gefunden , können Sie den 'WindowsFormsSyncronizationContext' zur späteren Verwendung speichern. Ich weiß nicht, wie es zu einem anderen "SyncronizationContext" geschaltet wird, aber vielleicht ist die Excel-DNA-Umgebung ... etwas harte Debugging-Zeit. – kennyzx

Antwort

7

Ihr Start() Methode muss von Code aufgerufen werden, um korrekt zu ermöglichen, sich auf dem UI-Thread läuft arbeiten die Background zu:

EDIT

Start() wird wie folgt aufgerufen. Es war nicht, wenn du diese Ausnahme bekommst. In Schutz-Code in Ihrer Methode, so dass Sie diese Panne diagnostizieren:

public void Start() 
    { 
     if (Thread.CurrentThread.GetApartmentState() != ApartmentState.STA) { 
      throw new InvalidOperationException("Bug! Code called from a worker thread"); 
     } 
     backgroundWorker.RunWorkerAsync(); 
    } 

Jetzt können Sie einen Haltepunkt in der throw-Anweisung gesetzt und verwenden den Aufruf der Debugger-Stack-Fenster, um herauszufinden, warum dies geschehen ist.

+0

InvokeRequired ist immer falsch für mich. –

+0

Das bedeutet, dass das UIProgress-Objekt auf dem falschen Thread erstellt wird. Code aktualisiert Es ist universeller als veröffentlicht, aber Sie erhalten eine bessere Stack-Trace, wenn Sie den Test in Ihrem Konstruktor setzen. –

1

Verwenden Sie die BeginInvoke() Methode auf dem Formular:

// http://msdn.microsoft.com/en-us/library/0b1bf3y3(v=vs.110).aspx

private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     this.BeginInvoke(new InvokeDelegate(InvokeMethod));    
    } 

    public delegate void InvokeDelegate(); 

    public void InvokeMethod() 
    { 
     this.Hide(); 
     this.Dispose(); 
    } 
3

Sie sind auf Hintergrundthread UI Operation aufrufen. Dies ist der Grund für diese Ausnahme. Ich würde ganz andere Methode verwenden, um den Fortschritt zu machen, ist die beste Aufgabe mit IProgress zu verwenden. Der andere Weg, es diese verwenden: `private void backgroundWorker_ProgressChanged (object sender, ProgressChangedEventArgs e) {

this.UpdateOnMainThread(
    () => 
    { 
     wdgProgressBar.Value = e.ProgressPercentage; 
     if (this.Visible == false) 
     { 
     this.ShowDialog(); 
     this.Update(); 
     } 
    }); 
} 

private void UpdateOnMainThread(Action action) 
{ 
    if (this.InvokeRequired) 
    { 
    this.BeginInvoke((MethodInvoker) action.Invoke); 
    } 
    else 
    { 
    action.Invoke(); 
    } 
} 

private void backgroundWorker_RunWorkerCompleted(object sender , RunWorkerCompletedEventArgs e) 
{ 
    this.UpdateOnMainThread(
    () => 
    { 
     this.Hide(); //Here I get a InvalidOperationException 
     this.Dispose(); 
    }); 

}` 
1

Ich glaube, Sie etwas Hilfe finden Sie hier: BackgroundWorker hide form window upon completion. Vergessen Sie jedoch nicht, BackgroundWorker Ereignisse zu lösen und BackgroundWorker zu stoppen, wie hier erklärt: Proper way to Dispose of a BackGroundWorker. Das Problem kann

this.Dispose(); 

im backgroundWorker_RunWorkerCompleted Ereignisse in der sein. Damit entsorgst du die Formularseite. Wollen Sie das tun? Oder möchten Sie BackgroundWorker entsorgen? Disposing Formularseite alle Ressourcen freigegeben werden so tun this.Hide(); ein zweites Mal kann ein Fehler sein.

Für weitere Informationen, können Sie diese Links: C# Form.Close vs Form.Dispose und Form.Dispose Method

1

Sie Führen Sie einen Aufruf des Hauptthreads aus einem Thread aus, der die Benutzeroberfläche nicht bearbeiten kann. Der einfachste Weg besteht darin, anonyme Delegate-Aufrufe zu verwenden. diese

Wechsel:

 if (this.Visible == false) 
     { 
      this.ShowDialog(); 
      this.Update(); 
     } 

Dazu:

this.Invoke((MethodInvoker) delegate { 
     if (this.Visible == false) 
     { 
      this.ShowDialog(); 
      this.Update(); 
     }  
}); 

Es ist nicht die Art und Weise optimiert, sondern macht den Job awesomely schnell ohne viel Recode. :)

Verwandte Themen