3

我在C#中使用後臺工作人員。 這裏是一個類,並根據它,你會發現它的instansiation,並在那裏我將定義我的問題給你:c#opennetCF後臺工作人員 - e.result給出一個ObjectDisposedException

我有類圖:

class Drawing 
{ 
    BackgroundWorker bgWorker; 
    ProgressBar progressBar; 
    Panel panelHolder; 

    public Drawing(ref ProgressBar pgbar, ref Panel panelBig) // Progressbar and panelBig as reference 
    { 
     this.panelHolder = panelBig; 
     this.progressBar = pgbar; 
     bgWorker = new BackgroundWorker(); 
     bgWorker.WorkerReportsProgress = true; 
     bgWorker.WorkerSupportsCancellation = true; 

     bgWorker.DoWork += new OpenNETCF.ComponentModel.DoWorkEventHandler(this.bgWorker_DoWork); 
     bgWorker.RunWorkerCompleted += new OpenNETCF.ComponentModel.RunWorkerCompletedEventHandler(this.bgWorker_RunWorkerCompleted); 
     bgWorker.ProgressChanged += new OpenNETCF.ComponentModel.ProgressChangedEventHandler(this.bgWorker_ProgressChanged); 
    } 

    public void createDrawing() 
    { 
     bgWorker.RunWorkerAsync(); 
    } 

    private void bgWorker_DoWork(object sender, DoWorkEventArgs e) 
    { 
     Panel panelContainer = new Panel(); 

      // Adding panels to the panelContainer 
      for(i=0; i<100; i++) 
      { 
      Panel panelSubpanel = new Panel(); 
      // Setting size, color, name etc.... 

      panelContainer.Controls.Add(panelSubpanel); // Adding the subpanel to the panelContainer 

      //Report the progress 
      bgWorker.ReportProgress(0, i); // Reporting number of panels loaded 
      } 

      e.Result = panelContainer; // Send the result(a panel with lots of subpanels) as an argument 
    } 

    private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) 
    { 
      this.progressBar.Value = (int)e.UserState; 
      this.progressBar.Update(); 
    } 

    private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     if (e.Error == null) 
     { 
      this.panelHolder   = (Panel)e.Result; 
     } 
     else 
     { 
      MessageBox.Show("An error occured, please try again"); 
     } 
    } 

} 

Instansiating的對象這個類:

public partial class Draw: Form 
{ 
    public Draw() 
    { 


     ProgressBar progressBarLoading = new ProgressBar(); 
     // Set lots of properties on progressBarLoading 

     Panel panelBigPanelContainer = new Panel();   

     Drawing drawer = new Drawing(ref progressBarLoading, ref panelBigPanelContainer); 

     drawer.createDrawing(); // this makes the object start a new thread, loading all the panels into a panel container, while also sending the progress to this progressbar. 
    } 

} 

這裏是我的問題: 在私人無效bgWorker_RunWorkerCompleted(對象發件人,RunWorkerCompletedEventArgs E)

我沒有得到e.Result,因爲它應該是。 當我調試,並期待在e.Result,面板屬性有此異常的消息:

'((System.Windows.Forms.Control)(e.Result)).ClientSize' threw an exception of type 'System.ObjectDisposedException' 

因此,對象被佈置,但「爲什麼」是我的問題,我怎麼能解決這個問題?

我希望有人會回答我,這讓我瘋狂。 我有另一個問題:是否允許使用帶參數的「ref」?這是不好的編程?

在此先感謝。

我也寫我如何理解下面的背景工人在這裏:


這就是我認爲是 「規則」 的背景工人:

bgWorker.RunWorkerAsync(); => starts a new thread. 
bgWorker_DoWork cannot reach the main thread without delegates 

-

private void bgWorker_DoWork(object sender, DoWorkEventArgs e) 
{ 
     // The work happens here, this is a thread that is not reachable by 
      the main thread 

     e.Result => This is an argument which can be reached by 
        bgWorker_RunWorkerCompleted() 


     bgWorker.ReportProgress(progressVar); => Reports the progress to the 
               bgWorker_ProgressChanged()   

} 

-

private void bgWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) 
    { 
      // I get the progress here, and can do stuff to the main thread from here 
       (e.g update a control) 

       this.ProgressBar.Value = e.ProgressPercentage; 
    } 

-

private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) 
    { 
     // This is where the thread is completed. 
     // Here i can get e.Result from the bgWorker thread 
     // From here i can reach controls in my main thread, and use e.Result in my main thread 


     if (e.Error == null) 
     { 
      this.panelTileHolder = (Panel)e.Result; 

     } 
     else 
     { 
      MessageBox.Show("There was an error"); 
     } 
    } 

回答

0

您在不同的線程創建UI控件(面板),並返回容器板回主線程。 UI控件具有線程關聯。當後臺工作完成時,它使用的線程被釋放回線程池,並且在該進程中,與該線程相關聯的UI控件顯然被丟棄。稍後當您嘗試在主線程中的RunWorkerCompleted事件處理程序中使用已放置的面板對象時,將得到ObjectDisposedException。

您需要在您的UI的主線程中創建這些面板。您可以在主線程中運行的ProgressChanged事件處理程序中創建它們,或者您可以調用另一種方法來檢查InvokeRequired是否已啓動,然後通過調用Invoke方法調用主線程上的操作。您可以隱藏這些面板,直到所有面板均已創建,並且在RunWorkerCompleted事件處理程序中可以顯示它們。

我建議你看看下面的blogpost。

WinForms UI Thread Invokes: An In-Depth Review of Invoke/BeginInvoke/InvokeRequred

+0

我希望能進不了調用,但它看起來像我不得不這樣做的.... – Ikky 2010-03-27 17:13:22

1

我跟不上你的代碼,「imagePanel」似乎從天上掉下來的,沒有它是怎麼產生的任何概念。但是你所做的事情是非常非法的,Windows需要一個控件的Parent(由你的Controls.Add()調用設置)成爲一個窗口,該窗口與子代在同一個線程中創建。 .NET 2.0通常會檢查這一點,並在違反該規則時生成IllegalOperationException,很難猜測爲什麼他們會將這些從CF中排除。如果他們實際上做到了。

當其RunWorkerCompleted或ProgressChanged事件運行並且表單已關閉時,ObjectDisposedException在BackgroundWorker中很常見。在您允許表單消失之前,您必須確保取消BGW。這在這裏有點不相干,你必須徹底重新設計這個。

+0

我的錯誤.... 「imagePanel」應爲「panelContainer」 – Ikky 2010-03-27 17:14:49

+0

我馬上去無論如何調用這個......現在我只是讀了一些使用Background worker的地方正在爲你調用。 我以前正在使用普通的線程,但我的一個朋友告訴我,後臺工作人員太棒了,我不得不嘗試它... – Ikky 2010-03-27 17:16:40

+0

當你做了很多調用並且實際運行大部分UI線程上的代碼。然後你只是放慢你的代碼。很多。詢問你的朋友。 – 2010-03-27 17:37:26