3

我是在 C# 中与后台工作人员一起工作的新手。这是一个类,在它下面,你会发现它的实例化,在下面我会为你定义我的问题:

我有类绘图:

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

}

这是我的问题: 在私有 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 之外。如果他们真的这样做了。

当其 RunWorkerCompleted 或 ProgressChanged 事件运行并且窗体已关闭时,ObjectDisposedException 与 BackgroundWorker 很常见。在允许表单消失之前,您始终必须确保取消 BGW。这在这里有点无关紧要,无论如何你都必须完全重新设计它。

于 2010-03-27T14:48:06.033 回答
0

You are creating UI controls (Panel) on a different thread and returning the container panel back to the main thread. UI controls have thread affinity. When the backgroundworker completes, the thread it uses gets released back to the thread pool and during that process, the UI controls associated with that thread are apparently disposed. When later you attempt to use a disposed panel object in RunWorkerCompleted event handler in your main thread, you get ObjectDisposedException.

You need to create those panels in your main thread where your UI is. You can create them in ProgressChanged event handler which runs in the main thread or you can call a different method that checks if InvokeRequired and if it does, then invokes the operation on the main thread by calling Invoke method. You can hide these panels until all of them are created and in RunWorkerCompleted event handler you can show them.

I suggest you take a look at the below blogpost.

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

于 2010-03-27T14:31:05.680 回答