2011-06-12 4 views
5

J'ai besoin d'aide pour essayer de comprendre ce que je fais mal. J'essaie d'obtenir une collection d'éléments à partir du journal système sur un thread distinct pour empêcher le gel du formulaire pendant le processus de collecte. Je peux obtenir le travailleur d'arrière-plan pour les saisir tous, mais je vais avoir quelques problèmes les ajouter au ListBox sur le formulaire.Travailler avec des threads C#

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 

    foreach (System.Diagnostics.EventLogEntry entry in eventLog1.Entries) 
    { 
    listBox1.Items.Add(
     entry.EntryType.ToString() + " - " + 
     entry.TimeWritten + "  - " + 
     entry.Source); 
    } 
} 

Il est évident que cela ne fonctionne pas comme prévu, car il y a 2 threads séparés, et vous ne pouvez pas changer d'objets sur différents threads, comme je l'ai découvert. Donc, si quelqu'un pouvait me guider dans la bonne direction, je serais reconnaissant.

+1

Jetez un oeil sur cette réponse: http://stackoverflow.com/questions/1136399/how-to -update-textbox-sur-gui-de-un-autre-thread-en-C# – Klinger

Répondre

4

Vous ne devez pas accéder aux éléments de l'interface utilisateur à partir d'un thread non UI. Exécutez ReportProgress, qui sera synchronisé avec le thread d'interface utilisateur.

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    foreach (System.Diagnostics.EventLogEntry entry in eventLog1.Entries) 
    { 
     var newEntry = entry.EntryType + " - " + entry.TimeWritten + "  - " + entry.Source; 
     backgroundWorker1.ReportProgress(0, newEntry); 
    } 
} 

void backgroundWorker1_ProgressChanged(object sender, ProgressChangedEventArgs e) 
{ 
    var newEntry = (string)e.UserState; 
    listBox1.Items.Add(newEntry); 
} 

Assurez-vous d'avoir activé WorkerReportsProgress.

backgroundWorker1.WorkerReportsProgress = true; 

et souscrit à ProgressChanged

backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged; 

Une autre approche consiste à appeler Control.Invoke intérieur

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e) 
{ 
    foreach (System.Diagnostics.EventLogEntry entry in eventLog1.Entries) 
    { 
     var newEntry = entry.EntryType.ToString() + " - " + entry.TimeWritten + "  - " + entry.Source; 
     Action action =() => listBox1.Items.Add(newEntry); 
     Invoke(action); 
    } 
} 

Mais cette approche ne vous ont pas besoin BackgroundWorker comme point de l'ensemble de celui-ci est d'utiliser ProgressChanged et RunWorkerCompleted gestionnaire d'événements qui sont synchronisés avec le thread UI.

1

Vous devez utiliser l'option de progression du rapport du travailleur d'arrière-plan. google this

0

Seul le thread graphique est autorisé à modifier les éléments de l'interface graphique. Si vous ne respectez pas cette règle, vous obtiendrez une exception. Vous pouvez:

  1. utiliser un MethodInvoker pour déclencher un appel à partir du fil GUI
  2. utiliser la fonction de progression du rapport (qui ne fait 1. Pour vous)
  3. stocker les objets dans une autre structure de données (verrouillage) et utilisez une minuterie dans le thread GUI pour tirer les objets et les afficher.
1

Il ne devrait pas y avoir de problèmes que vous utilisez BackgroundWorker. Tout l'appel à la méthode de rappel s'exécute sur le même contexte d'interface utilisateur.

EDIT:

si vous voulez faire état de progrès, vous devez stocker SynchronizationContext.Current au démarrage de préférence. ou vous pouvez utiliser le motif IsInvokeRequired. Voici comment j'utilise SynchronizationContext

private SynchronizationContext uiContext; 
     public Form1() 
     { 
      uiContext = SynchronizationContext.Current; 
      InitializeComponent(); 
      FillItem(); 
     } 

je code suivant, et il fonctionne comme un charme.

public void FillItem() 
      { 
       BackgroundWorker worker = new BackgroundWorker(); 
       worker.WorkerReportsProgress = true; 
       worker.DoWork += (a, b) => 
            { 
             int i = 0; //Percentage complete, roll your own logic. 
             foreach (var eventLog in EventLog.GetEventLogs()) 
             { 
              foreach (EventLogEntry entry in eventLog.Entries) 
              { 
               this.listBox1.Items.Add(entry.Message); 
uiContext.Post(z=>worker.ReportProgress(i++),null); 

              } 
             } 


            }; 
       worker.RunWorkerAsync(); 
       worker.ProgressChanged += (a, b) => this.progressBar1.Value = b.ProgressPercentage; 


      } 
1

Essayez ceci, de manière très simple d'invoquer une action sur le thread du contrôle:

private void Form1_Load(object sender, EventArgs e) 
{ 
    var bw = new BackgroundWorker(); 
    bw.DoWork += DoWork; 
    bw.RunWorkerAsync(); 
} 
private void DoWork(object sender, DoWorkEventArgs e) 
{ 
    var itemList = new List<int> {1, 22, 3, 4}; 
    var func = new Action<int>(itemToAdd => listBox1.Items.Add(itemToAdd)); 
    foreach (var item in itemList) 
    { 
     listBox1.Invoke(func, item); 
    } 
}