2010-03-15 2 views
8

Ho un lavoratore in background. Prima di invocare il lavoratore, disabilito un pulsante e rendi visibile una gif. Quindi invoco il metodo runworkerasync e funziona bene fino al comleteion. Su 'RunWorkerCompleted()' ottengo un errore cross thread. Qualche idea del perché?BackgroundWorker Non funziona in VSTO

private void buttonRun_Click(object sender, EventArgs e) 
    { 
     if (comboBoxFiscalYear.SelectedIndex != -1 && !string.IsNullOrEmpty(textBoxFolderLoc.Text)) 
     { 
      try 
      { 
       u = new UpdateDispositionReports(
        Convert.ToInt32(comboBoxFiscalYear.SelectedItem.ToString()) 
        , textBoxFolderLoc.Text 
        , Properties.Settings.Default.TemplatePath 
        , Properties.Settings.Default.ConnStr); 
       this.buttonRun.Enabled = false; 
       this.pictureBox1.Visible = true; 

       BackgroundWorker bw = new BackgroundWorker(); 
       bw.DoWork += new DoWorkEventHandler(bw_DoWork); 
       bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted); 
       bw.RunWorkerAsync(); 
       //backgroundWorker1.RunWorkerAsync(); 
      } 
      catch (Exception ex) 
      { 
       MessageBox.Show("Unable to process.\nError:" + ex.Message, Properties.Settings.Default.AppName); 
      } 
     } 
    } 

    void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     buttonRun.Enabled = true; 
     pictureBox1.Visible = false; 
    } 

    void bw_DoWork(object sender, DoWorkEventArgs e) 
    { 
     u.Execute(); 
    } 
+1

Si prega di inviare la traccia dello stack. –

risposta

2

qualcosa circa VSTO in esecuzione il lavoratore sfondo sullo stesso thread come i controlli. Non sono sicuro. Ho dovuto controllare la InvokeRequired

private void buttonRun_Click(object sender, EventArgs e) 
    { 
     if (comboBoxFiscalYear.SelectedIndex != -1 && !string.IsNullOrEmpty(textBoxFolderLoc.Text)) 
     { 
      try 
      { 
       u = new UpdateDispositionReports(
        Convert.ToInt32(comboBoxFiscalYear.SelectedItem.ToString()) 
        , textBoxFolderLoc.Text 
        , Properties.Settings.Default.TemplatePath 
        , Properties.Settings.Default.ConnStr); 
       this.buttonRun.Enabled = false; 
       this.pictureBox1.Visible = true; 

       BackgroundWorker bw = new BackgroundWorker(); 
       bw.DoWork += new DoWorkEventHandler(bw_DoWork); 
       bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bw_RunWorkerCompleted); 
       bw.RunWorkerAsync(); 
       //backgroundWorker1.RunWorkerAsync(); 
      } 
      catch (Exception ex) 
      { 
       MessageBox.Show("Unable to process.\nError:" + ex.Message, Properties.Settings.Default.AppName); 
      } 
     } 
    } 
    delegate void ReenableRunCallback(); 

    private void ReenableRun() 
    { 
     if (this.buttonRun.InvokeRequired) 
     { 
      ReenableRunCallback r = new ReenableRunCallback(ReenableRun); 
      this.buttonRun.Invoke(r, null); 
     } 
     else 
      this.buttonRun.Enabled = true; 
    } 
    private void HideProgress() 
    { 
     if (this.pictureBox1.InvokeRequired) 
     { 
      ReenableRunCallback r = new ReenableRunCallback(HideProgress); 
      this.pictureBox1.Invoke(r, null); 
     } 
     else 
      this.pictureBox1.Visible = false; 
    } 

    void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     ReenableRun(); 
     HideProgress(); 
    } 

    void bw_DoWork(object sender, DoWorkEventArgs e) 
    { 
     u.Execute(); 
    } 
+2

+1 Il tuo pulsante è stato inizialmente creato dal tuo primo thread principale dell'applicazione, noto anche come thread della GUI. Quindi, su RunWorkerCompleted, si tenta di accedere al pulsante che è stato creato dal thread precedente. Quindi, è necessario verificare se è possibile accedere al pulsante con la proprietà InvoqueRequired, quindi riprovare tramite un delegato invocato come hai fatto, per sincronizzare entrambi i thread. Questo è il modo! –

18

Sembra essere un problema con VSTO e BackgroundWorker.

La soluzione è here.

Fondamentalmente è necessario chiamare

System.Threading.SynchronizationContext.SetSynchronizationContext(new WindowsFormsSynchronizationContext()); 

prima di chiamare RunWorkerAsync. Funziona alla grande.

Per evitare di creare un'istanza dell'oggetto ogni volta che si può avere un membro statico nella classe principale di AddIn e riutilizzarlo. In questo modo puoi istanziare solo una volta.

+1

Questo mi ha salvato il weekend. – squillman

+0

Mi ha salvato anche qualche ora. – OfficeAddinDev

+0

Seriamente? Un consiglio eccellente, ma trovare quella soluzione ha ucciso la maggior parte del mio pomeriggio. Ho bisogno di una birra !! –