2009-07-28 4 views
9

Je travaille en C# et WPF, très nouveau pour les deux.Comment afficher la progression pendant une boucle occupée?

J'ai une boucle qui lit beaucoup de données d'une source externe. Le processus prend environ 20 secondes, et je veux montrer la progression à l'utilisateur. Je n'ai pas besoin de barres de progression fantaisie, j'ai donc choisi de tracer ma progression dans une étiquette qui dira "Step 1/1000", puis passer à "Step 2/1000" etc.

Mon code ressemble à quelque chose comme ceci:

// "count" is the number of steps in the loop, 
// I receive it in previous code 

String countLabel = "/"+count.ToString(); 

for (i = 0; i < count; i++) 
{ 
    ... do analysis ... 
    labelProgress.Content = "Step "+i.ToString()+countLabel 
} 

Cependant, au cours de cette analyse, l'écran est « bloqué » et les progrès ne montre pas avancer. Je comprends ce comportement de mon passé en C++, où j'aurais probablement un thread séparé montrant une barre de progression recevant des notifications de la boucle, ou une forme de repeindre/rafraîchir, ou forçant la fenêtre/application à traiter sa file de messages.

Quelle est la bonne façon de le faire en C#? Je ne suis pas attaché à l'étiquette, donc s'il y a un écran simple pop-up progrès-bar que je pouvais utiliser au lieu de ce label, il serait aussi grand ...

Merci

+0

Cela peut être l'aide pour vous , ** 100% testé ** http://stackoverflow.com/a/42995210/6863414 –

Répondre

11

Déplacer le travail à un BackgroundWorker et utilisez la méthode ReportProgress.

for (i = 0; i < count; i++) 
{ 
    ... do analysis ... 
    worker.ReportProgress((100 * i)/count); 
} 

private void MyWorker_ProgressChanged(object sender, 
    ProgressChangedEventArgs e) 
{ 
    taskProgressBar.Value = Math.Min(e.ProgressPercentage, 100); 
} 
+0

Est-ce thread-safe? –

+1

Oui, ça devrait l'être. Votre travailleur d'arrière-plan est uniquement dans un thread, la mise à jour de la barre de progression est uniquement sur le thread d'interface utilisateur. Tout thread autre que le thread d'interface utilisateur lèvera une exception s'il essaie de mettre à jour la valeur. Vous êtes donc sûr de la sécurité des threads avec cette méthode. – user7116

2

L'interface utilisateur est mis à jour pas en raison du fait que votre thread courant a une priorité plus élevée que votre thread d'interface utilisateur qui définira finalement l'étiquette;). Donc, jusqu'à ce que votre discussion soit terminée, votre étiquette sera mise à jour à la fin.

Heureusement pour nous, il y a une propriété Dispatcher sur chaque contrôle WPF qui vous permet de feu vers le haut un nouveau thread avec une autre priorité ..

labelProgress.Dispatcher.Invoke(DispatcherPriority.Background, 
        () => labelProgress.Content = string.Format("Step {0}{1}", i, countLabel)); 

Ce feu un fil en arrière-plan et obtiendrait le travail terminé! Vous pouvez également essayer d'autres options de DispatcherPriority

PS J'ai aussi pris la liberté d'ajouter une méthode anonyme et fixer votre chaîne un peu l'analyse syntaxique .. espérons que vous ne me dérange pas ..

3
//Create a Delegate to update your status button 
    delegate void StringParameterDelegate(string value); 
    String countLabel = "/" + count.ToString(); 
    //When your button is clicked to process the loops, start a thread for process the loops 
    public void StartProcessingButtonClick(object sender, EventArgs e) 
    { 
     Thread queryRunningThread = new Thread(new ThreadStart(ProcessLoop)); 
     queryRunningThread.Name = "ProcessLoop"; 
     queryRunningThread.IsBackground = true; 
     queryRunningThread.Start(); 
    } 

    private void ProcessLoop() 
    { 
     for (i = 0; i < count; i++) 
     { 
      ... do analysis ... 
      UpdateProgressLabel("Step "+i.ToString()+countLabel); 
     } 
    } 

    void UpdateProgressLabel(string value) 
    { 
     if (InvokeRequired) 
     { 
      // We're not in the UI thread, so we need to call BeginInvoke 
      BeginInvoke(new StringParameterDelegate(UpdateProgressLabel), new object[] { value }); 
      return; 
     } 
     // Must be on the UI thread if we've got this far 
     labelProgress.Content = value; 
    } 
+0

BackgroundWorker est fourni afin que vous n'ayez pas à gérer tout le gâchis d'engendrer un fil. – user7116

+0

@sixlettervariables La valeur de cette méthode est qu'elle vous permet plus de flexibilité que de simplement renvoyer un entier à l'appelant - vous pouvez faire N'IMPORTE QUOI (comme renvoyer un message d'état, par exemple) – ean5533

Questions connexes