2010-02-04 4 views
2

J'ai un programme qui prend 10-20 secondes pour démarrer. J'ai besoin de montrer une fenêtre avec une barre de progression quand le programme démarre. Je sais que BackgroundWorker est la bonne façon de le faire mais je n'ai malheureusement pas le temps de retravailler tout le gui en utilisant les threads en ce moment. Voici du code que j'essaie mais ça ne marche pas. Quelqu'un peut-il aider ..?C#, WPF, Mise à jour du gui sans backgroundworkers

using System; 
using System.Threading; 
using System.Windows; 

namespace WpfApplication1 
{ 
    public partial class Window1 : Window 
    { 
     public Window1() 
     { 
      InitializeComponent(); 

      Thread t = new Thread(ShowLoadingWindow); 
      t.SetApartmentState(ApartmentState.STA); 
      t.Priority = ThreadPriority.Highest; 
      t.Start(); 

      DoSomeLongTask(); 
      keepLooping = false; 
     } 

     bool keepLooping = true; 
     private void ShowLoadingWindow() 
     { 
      LoadingWindow lw = new LoadingWindow(); 
      lw.Show(); 

      while (keepLooping) 
      { 
       Thread.Sleep(1); 
      } 

      lw.Close(); 
     } 

     private void DoSomeLongTask() 
     { 
      for (int i = 0; i < 20000; i++) 
      { 
       GC.Collect(); 
      } 
     } 
    } 
} 

La fenêtre de chargement est juste une fenêtre vide avec une barre de progression. Comment cela ne fonctionne-t-il pas?

+6

Vous devez rester avec BackgroundWorker. Il a les hooks nécessaires pour le faire correctement, et à la longue, il sera plus simple et plus rapide de coder que d'essayer de rouler sa propre solution de threading. Vous n'avez pas besoin de retravailler toute l'interface graphique pour le faire fonctionner; vous avez juste besoin d'une tâche d'arrière-plan à exécuter dans le worker. –

+0

Totalement d'accord avec Robert. Je n'ai aucune idée de la façon dont vous envisagez de mettre à jour la barre de progression avec le code indiqué ci-dessus, mais BackgroundWorker le rend trivial. –

Répondre

10

Vous faites toujours la longue tâche dans le fil principal. Vous devez montrer la fenêtre de chargement dans le fil principal et faire la longue tâche en arrière-plan.

L'utilisation d'un BackgroundWorker est vraiment la solution la plus simple pour cela:

 
BackgroundWorker bw; 
LoadingWindow lw; 

public Window1() { 
    bw = new BackgroundWorker(); 
    lw = new LoadingWindow(); 

    bw.DoWork += (sender, args) => { 
     // do your lengthy stuff here -- this will happen in a separate thread 
     ... 
    } 

    bw.RunWorkerCompleted += (sender, args) => { 
     if (args.Error != null) // if an exception occurred during DoWork, 
      MessageBox.Show(args.Error.ToString()); // do your error handling here 

     // close your loading window here 
     ... 
    } 

    // open and show your loading window here 
    ... 

    bw.RunWorkerAsync(); // starts the background worker 
} 
+0

+1 J'ai posté le correctif pour son code, mais le vôtre est probablement la meilleure solution. – Ian

5

Il est vraiment très facile à utiliser BackgroundWorker.

public partial class Window1 : Window 
{ 
    BackgroundWorker worker = new BackgroundWorker(); 

    public Window1() 
    { 
     worker.DoWork += new DoWorkEventHandler(worker_DoWork); 
     worker.ReportsProgress = true; 
     worker.ProgressChanged += new ProgressChangedEventHandler(update_progress); 
    } 


    void worker_DoWork(object sender, DoWorkEventArgs e){ 
     DoSomeLongTask(); 
     //call worker.ReportProgress to update bar 
    } 

    void update_progress(object sender, ProgressChangedEventArgs e) 
    { 
     myscrollbar.Value = e.Value; 
    } 
} 

La clé à garder à l'esprit est de ne jamais toucher les trucs de la méthode DoWork. Cela doit à travers ProgressChanged/ReportProgress

+1

+1 pour les choses update_progress. – Heinzi

+0

Je pense que celui-ci devrait être marqué comme une réponse. Il m'a donné la réponse à mon problème et cette réponse est comme super simple. Vraiment super morceau de code d'exemple. – Pijusn

3

Si vous ne voulez pas le travailleur d'arrière-plan, vous devez ajuster votre code, pour faire la longue tâche dans le nouveau thread.

using System; 
using System.Threading; 
using System.Windows; 

namespace WpfApplication1 
{ 
    public partial class Window1 : Window 
    { 
     public Window1() 
     { 
      InitializeComponent(); 

      Thread t = new Thread(DoSomeLongTask); 
      t.Start(); 

      // switch this with the DoSomeLongTask, so the long task is 
      // on the new thread and the UI thread is free. 
      ShowLoadingWindow(); 
    } 
    } 
} 

Si vous souhaitez mettre à jour puis une barre de progression de votre méthode « DoSomeLongTask » alors vous devez vous assurer que vous appelez Invoke. Par exemple:

delegate void ProgressDelegate(int Progress); 
private void UpdateProgress(int Progress) 
{ 
    if (!progressBar1.Dispatcher.CheckAccess()) 
    { 
    progressBar1.Value = Progress; 
    } 
    else 
    { 
    ProgressDelegate progressD = new ProgressDelegate(UpdateProgress); 
    progressBar1.Dispatcher.Invoke(progressD, new object[] { Progress }); 
    } 
} 
+0

+1 pour montrer comment améliorer la solution existante. BTW: 'keepLooping = false' doit être déplacé à la fin de' DoSomeLongTask() ', IMO, pour s'assurer que' ShowLoadingWindow() 'se termine. – Heinzi

+0

lw.Close() doit être appelé dans le thread d'interface utilisateur, plutôt que le nouveau thread, donc ça devrait aller. La mise à jour de la barre de progression est une autre affaire, alors j'ai mis à jour ma réponse. – Ian

+0

Vous avez raison, bien sûr. J'ai mis à jour mon commentaire avant de lire votre réponse. :-) – Heinzi

Questions connexes