3

Je suis nouveau travail avec un travailleur de fond en C#. Voici une classe, et en dessous, vous trouverez la instansiation de celui-ci, et là-dessous je définirai mon problème pour vous:c travailleur de fond # de OpenNETCF - e.result donne une ObjectDisposedException

Je le dessin de la classe:

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 un objet de cette classe:

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

} 

Voici mon problème: Dans le vide privé bgWorker_RunWorkerCompleted (expéditeur d'objet, RunWorkerCompletedEventArgs e)

Je ne reçois pas le résultat comme il se doit. Quand je regarde le débogage et le e.Result, les propriétés du panneau ont ce message d'exception:

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

Ainsi, l'objet se placé, mais « pourquoi » est ma question, et comment puis-je résoudre ce problème?

J'espère que quelqu'un va me répondre, cela me rend fou. Une autre question que j'ai: Est-il permis d'utiliser "ref" avec des arguments? est-ce une mauvaise programmation?

Merci d'avance.

J'ai aussi écrit comment je comprends le travailleur de fond ci-dessous ici:


Ce que je pense est les "règles" pour les travailleurs de fond:

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

Répondre

0

Vous créez des contrôles de l'interface utilisateur (panneau) sur un thread différent et retourner le panneau de conteneurs au thread principal. Les contrôles de l'interface utilisateur ont une affinité de thread. Lorsque le backgroundworker est terminé, le thread qu'il utilise est libéré dans le pool de threads et, au cours de ce processus, les contrôles de l'interface utilisateur associés à ce thread sont apparemment éliminés. Lorsque plus tard vous essayez d'utiliser un objet de panneau éliminé dans le gestionnaire d'événements RunWorkerCompleted dans votre thread principal, vous obtenez ObjectDisposedException.

Vous devez créer ces panneaux dans votre thread principal où votre interface utilisateur est. Vous pouvez les créer dans le gestionnaire d'événements ProgressChanged qui s'exécute dans le thread principal ou vous pouvez appeler une méthode différente qui vérifie si InvokeRequired et, le cas échéant, appelle l'opération sur le thread principal en appelant la méthode Invoke. Vous pouvez masquer ces panneaux jusqu'à ce qu'ils soient tous créés et dans le gestionnaire d'événements RunWorkerCompleted, vous pouvez les afficher.

Je vous suggère de jeter un coup d'oeil au-dessous blogpost.

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

+0

J'espérais pas entrer dans d'invoquer, mais il semble que je dois le faire de cette façon .... – Ikky

1

Je ne peux pas suivre votre code, « ImagePanel » semble tomber du ciel sans idée comment il a créé. Ce que vous faites est cependant tout à fait illégal, Windows exige que le parent d'un contrôle (défini par votre appel Controls.Add()) soit une fenêtre créée dans le même thread que l'enfant. .NET 2.0 vérifie normalement ceci et génère une exception IllegalOperationException quand vous violez cette règle, difficile à deviner pourquoi ils laisseraient cela hors de CF. Si en fait ils l'ont fait.

ObjectDisposedException est commun à BackgroundWorker lorsque son événement RunWorkerCompleted ou ProgressChanged est exécuté et que le formulaire a été fermé. Vous devez toujours vous assurer d'annuler la BGW avant de permettre la disparition du formulaire. C'est un peu hors sujet ici, vous devez complètement le remodeler de toute façon.

+0

Mon erreur .... « ImagePanel » devrait être « panelContainer » – Ikky

+0

Je vais voir invoquant cela de toute façon maintenant ... c'est juste que j'ai lu quelque part que l'utilisation de Background worker faisait l'invocation pour vous. J'utilisais le fil régulier avant, mais un de mes amis m'a dit que le fond d'écran était si fantastique que je devais l'essayer ... – Ikky

+0

Ça cesse d'être fantastique quand on fait beaucoup d'invocation et qu'on exécute en fait la majorité des le code sur le thread UI. Ensuite, vous ralentissez votre code. Beaucoup. Demandez à votre ami à ce sujet. –

Questions connexes