c# opennetCF 后台工作者 - e.result 给出了 ObjectDisposeException

发布于 2024-08-26 14:29:59 字数 4654 浏览 8 评论 0原文

我是新来的 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.
  }

}

这是我的问题: 在 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");
        }
    }

I'm new working with background worker in C#.
Here is a class, and under it, you will find the instansiation of it, and under there i will define my problem for you:

I have the class 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");
        }
    }

}

Instansiating an object of this class:

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

}

Here is my problem:
In the private void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)

i don't get the e.Result as it should be.
When i debug and look at the e.Result, the panel's properties have this exception message:

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

So the object gets disposed, but "why" is my question, and how can i fix this?

I hope someone will answer me, this is making me crazy.
Another question i have: Is it allowed to use "ref" with arguments? is it bad programming?

Thanks in advance.

I have also written how i understand the Background worker below here:


This is what i think is the "rules" for background workers:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

策马西风 2024-09-02 14:30:00

我无法遵循你的代码,“imagePanel”似乎从天上掉下来,不知道它是如何创建的。然而,您所做的事情是相当非法的,Windows 要求控件的父级(由您的 Controls.Add() 调用设置)是与子级在同一线程中创建的窗口。 .NET 2.0 通常会检查这一点,并在您违反该规则时生成 IllegalOperationException,很难猜测为什么他们会将其排除在 CF 之外。如果他们真的这么做了。

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

I can't follow your code, "imagePanel" seems to fall from the sky without any notion how it got created. What you are doing is however quite illegal, Windows requires the Parent of a control (set by your Controls.Add() call) to be a window that was created in the same thread as the child. .NET 2.0 normally checks for this and generates an IllegalOperationException when you violate that rule, hard to guess why they would leave that out of CF. If they in fact did.

ObjectDisposedException is common with BackgroundWorker when its RunWorkerCompleted or ProgressChanged event runs and the form was closed. You always have to make sure to cancel the BGW before you allow the form to disappear. That's kinda irrelevant here, you have to completely redesign this anyway.

指尖上得阳光 2024-09-02 14:30:00

您正在不同的线程上创建 UI 控件(面板)并将容器面板返回到主线程。 UI 控件具有线程关联性。当后台工作程序完成时,它使用的线程将被释放回线程池,并且在此过程中,与该线程关联的 UI 控件显然会被释放。当稍后您尝试在主线程的 RunWorkerCompleted 事件处理程序中使用已处置的面板对象时,您将收到 ObjectDisposeException。

您需要在 UI 所在的主线程中创建这些面板。您可以在主线程中运行的 ProgressChanged 事件处理程序中创建它们,也可以调用不同的方法来检查是否为 InvokeRequired,如果是,则通过调用 Invoke 方法来调用主线程上的操作。您可以隐藏这些面板,直到所有面板都创建完成,并且可以在 RunWorkerCompleted 事件处理程序中显示它们。

我建议您看一下下面的博文。

WinForms UI 线程调用:Invoke/BeginInvoke/InvokeRequred 的深入回顾

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文