2011-10-05 2 views
0

J'ai récemment travaillé sur une application où je voulais afficher la progression d'un autre thread dans la barre d'état via le contrôle ToolStripProgressBar contenu dans le champ StatusBar. Avant que j'aie essayé d'ajouter ce code j'ai d'abord eu le code changeant le texte d'un contrôle ToolStripStatusLabel et pour ce faire j'ai utilisé la méthode Invoke avec des délégués et tout a bien fonctionné. Cependant, j'ai trouvé que quand j'ai essayé ceci avec le ToolStripProgressBar l'appel à la méthode Invoke de la barre d'état a échoué sans notification (aucune erreur, aucune exception, rien). Ce que j'ai appris depuis, c'est que pour utiliser une barre de progression de cette façon, j'ai dû utiliser un contrôle BackgroundWorker. Donc, mon code fonctionne, mais je ne comprends pas pourquoi je ne pouvais pas utiliser la méthode Invoke qui semblait déjà fonctionner.Pourquoi la méthode StatusBar.Invoke ne fonctionne pas pour un ToolStripProgressBar?

Quelques exemples de ce qui a fonctionné et ce qui n'a pas:

Cela a fonctionné

public delegate void changeStatusMessage(String message); 
public changeStatusMessage changeStatusMessageDelegate; 
public void changeStatusMessageMethod(String message){ 
     if(statusbar.InvokeRequired){ 
      statusbar.Invoke(changeStatusMessageDelegate, new Object[] {message}); 
     }else{ 
      toolstripLabel.Text = message; 
     } 
} 

Cela ne fonctionne pas

public delegate void incrementProgressBar(int value); 
public incrementProgressBar incrementProgressBarDelegate; 
public void incrementProgressBarMethod(int value){ 
     if(statusbar.InvokeRequired){ 
      statusbar.Invoke(incrementProgressBarDelegate, new Object[] {value}); 
     }else{ 
      toolstripProgress.Increment(value); 
     } 
} 

Dans l'exemple qui ne fonctionnait pas la propriété InvokeRequired est vrai, donc la méthode Invoke est appelée et rien ne se passe. Où, comme je m'y attendais, j'appelle à nouveau le incrementProgressBarMethod cette fois où InvokeRequired est faux et permettant ainsi à la méthode Increment de se déclencher.

Je voudrais vraiment savoir pourquoi cela ne fonctionne pas. Comme je l'ai dit, j'ai déjà rééquipé pour utiliser un BackgroundWorker, je veux juste une explication.

+1

Il n'y a rien d'un TSPB spécial qui vous remettra une explication facile. Control.Invoke() tend à se bloquer facilement si le thread principal est bloqué, donc "rien ne se passe" n'est pas rare. Stick avec BackgroundWorker. –

+0

Je n'ai jamais dit que je voulais une explication "Facile", les plus compliqués fonctionneraient aussi bien. Me dire simplement que Control.Invoke() peut causer une impasse ne me suffit pas vraiment pour comprendre la raison. Le Control.Invoke() est fourni pour une raison, pourquoi je ne peux pas l'utiliser pour ce contrôle unique, quand un contrôle géré de la même manière avec le même code fonctionne sans problème. – JRSofty

+0

Post code repro qui démontre le problème et n'importe qui peut tester et vous obtiendrez votre réponse. –

Répondre

0

Appelez les appels API postmessage et le message en file d'attente sur le message Windows. Si le thread UI est bloqué, alors vous pouvez avoir un blocage, car il ne peut pas pousser le message en file d'attente, rien ne se passera. L'autre côté de invoke requis n'est pas déclenché, et si quelque chose l'attend, bang, deadlock.

C'est la raison pour laquelle vous devez faire attention avec Invoke.

How to invoke a function on parent thread in .NET?

Mais votre problème est la création du délégué, il est un délégué nul, vous devez créer le délégué sur un même fil qu'il est appelé par Invoke, car autrement, le système de sous-fifre sera échouer lors du marshaling du délégué (c'est un pointeur).

private void changeStatusMessageMethod(String message) 
    { 
     if (this.InvokeRequired) 
     { 
      var changeStatusMessageDelegate = new changeStatusMessage(changeStatusMessageMethod); 
      this.Invoke(changeStatusMessageDelegate, new Object[] { message }); 
     } 
     else 
     { 
      toolstripLabel.Text = message; 
     } 
    } 
    delegate void incrementProgressBar(int value); 
    private void incrementProgressBarMethod(int value) 
    { 
     if (this.InvokeRequired) 
     { 
      var incrementProgressBarDelegate = new incrementProgressBar(incrementProgressBarMethod); 
      this.Invoke(incrementProgressBarDelegate, new Object[] { value }); 
     } 
     else 
     { 
      toolstripProgress.Increment(value); 
     } 
    } 

Cela fonctionne sur dotnet cadre v4

private void button1_Click(object sender, EventArgs e) 
    { 
     var t = new System.Threading.Thread(new System.Threading.ThreadStart(x)); 
     t.Start(); 
    } 

    private void x() 
    { 
     do 
     { 
      changeStatusMessageMethod(DateTime.Now.ToString()); 
      System.Threading.Thread.Sleep(1000); 
     } while (true); 
    } 

    private void button2_Click(object sender, EventArgs e) 
    { 
     var t = new System.Threading.Thread(new System.Threading.ThreadStart(y)); 
     t.Start(); 
    } 

    private void y() 
    { 
     do 
     { 
      incrementProgressBarMethod(1); 
      System.Threading.Thread.Sleep(1000); 
     } while (true); 
    } 
Questions connexes