3

私はC#でバックグラウンドワーカーを使用するのは初めてです。ここにクラスがあり、その下にそのインスタンスがあり、その下に私の問題を定義します。

私はクラスDrawingを持っています:

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");
        }
    }

}

このクラスのオブジェクトをインスタンス化する:

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

}

これが私の問題です:private void bgWorker_RunWorkerCompleted(object sender、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");
        }
    }
4

2 に答える 2

1

私はあなたのコードをたどることができません.「imagePanel」は、それがどのように作成されたかを知らずに空から落ちているようです. ただし、あなたがしていることは非常に違法です.Windowsは、コントロールの親( Controls.Add() 呼び出しによって設定されます)が、子と同じスレッドで作成されたウィンドウである必要があります。.NET 2.0 は通常これをチェックし、その規則に違反すると IllegalOperationException を生成します。なぜそれを CF から除外するのかを推測するのは困難です。彼らが実際にやった場合。

ObjectDisposedException は、RunWorkerCompleted または ProgressChanged イベントが実行され、フォームが閉じられたときに、BackgroundWorker と共通です。フォームを非表示にする前に、必ず BGW をキャンセルする必要があります。ここではちょっと関係ありませんが、とにかくこれを完全に再設計する必要があります。

于 2010-03-27T14:48:06.033 に答える
0

別のスレッドでUIコントロール(パネル)を作成し、コンテナーパネルをメインスレッドに戻しています。UIコントロールにはスレッドアフィニティがあります。バックグラウンドワーカーが完了すると、使用するスレッドがスレッドプールに解放され、そのプロセス中に、そのスレッドに関連付けられているUIコントロールが破棄されるようです。後でメインスレッドのRunWorkerCompletedイベントハンドラーで破棄されたパネルオブジェクトを使用しようとすると、ObjectDisposedExceptionが発生します。

UIがあるメインスレッドでこれらのパネルを作成する必要があります。それらは、メインスレッドで実行されるProgressChangedイベントハンドラーで作成できます。または、InvokeRequiredかどうかを確認する別のメソッドを呼び出し、必要な場合は、Invokeメソッドを呼び出してメインスレッドで操作を呼び出すことができます。これらのパネルは、すべて作成されるまで非表示にでき、RunWorkerCompletedイベントハンドラーで表示できます。

以下のブログ投稿をご覧になることをお勧めします。

WinForms UIスレッドの呼び出し:Invoke / BeginInvoke/InvokeRequredの詳細なレビュー

于 2010-03-27T14:31:05.680 に答える