c # opennetCF фоновый работник - e.result дает исключение ObjectDisposedException - PullRequest
3 голосов
/ 27 марта 2010

Я недавно работаю с фоновым работником в 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.
  }

}

Вот моя проблема: В приватной пустоте 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");
        }
    }

Ответы [ 2 ]

1 голос
/ 27 марта 2010

Я не могу следовать вашему коду, "imagePanel", кажется, падает с неба без какого-либо представления о том, как он был создан. Однако то, что вы делаете, совершенно недопустимо: Windows требует, чтобы родительский элемент управления (установленный вашим вызовом Controls.Add ()) был окном, созданным в том же потоке, что и дочерний элемент. .NET 2.0 обычно проверяет это и генерирует исключение IllegalOperationException, когда вы нарушаете это правило, трудно догадаться, почему они исключили бы это из CF. Если они действительно сделали.

ObjectDisposedException характерно для BackgroundWorker, когда его событие RunWorkerCompleted или ProgressChanged выполняется и форма закрыта. Вы всегда должны обязательно отменить BGW, прежде чем позволить форме исчезнуть. Это немного неважно, вам все равно придется полностью изменить дизайн.

0 голосов
/ 27 марта 2010

Вы создаете элементы управления пользовательского интерфейса (Panel) в другом потоке и возвращаете панель контейнера обратно в основной поток.Элементы управления пользовательского интерфейса имеют сходство потоков.Когда фоновый работник завершает работу, используемый им поток возвращается обратно в пул потоков, и в ходе этого процесса элементы управления пользовательского интерфейса, связанные с этим потоком, по-видимому, удаляются.Когда позже вы попытаетесь использовать удаленный объект панели в обработчике событий RunWorkerCompleted в своем основном потоке, вы получите ObjectDisposedException.

Вам нужно создать те панели в главном потоке, где находится ваш пользовательский интерфейс.Вы можете создать их в обработчике событий ProgressChanged, который выполняется в главном потоке, или вы можете вызвать другой метод, который проверяет, если InvokeRequired и если он это делает, затем вызывает операцию в основном потоке, вызывая метод Invoke.Вы можете скрывать эти панели до тех пор, пока они не будут созданы, и в обработчике событий RunWorkerCompleted вы можете их отобразить.

Я предлагаю вам взглянуть на приведенный ниже пост блога.

Поток пользовательского интерфейса WinFormsЗапускает: углубленный анализ Invoke / BeginInvoke / InvokeRequred

...