Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Working with threads C#

I need some help trying to figure what I'm doing wrong. I'm trying to get a collection of items from the system log on a separate thread to keep the form from being frozen during the collection process. I can get the background worker to grab them all, but I am having some issues add them to the ListBox on the form.

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

Obviously this doesn't work as expected, since there are 2 separate threads, and you can't change objects on different threads, as I have found out. So, If someone could guide me in the right direction, I would be thankful.

like image 220
Rekar Avatar asked Jun 12 '11 07:06

Rekar


1 Answers

You should not access UI elements from non-UI thread. Run ReportProgress, which will be synced with UI thread.

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

Make sure you enable WorkerReportsProgress.

backgroundWorker1.WorkerReportsProgress = true;

and subscribed to ProgressChanged

backgroundWorker1.ProgressChanged += backgroundWorker1_ProgressChanged;

Another approach is to call Control.Invoke inside

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

But with this approach you don't need BackgroundWorker as whole point of it is to use ProgressChanged and RunWorkerCompleted event handler which are synced with the UI thread.

like image 143
Alex Aza Avatar answered Oct 25 '22 18:10

Alex Aza