6

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.

1

5 Answers 5

4

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.

Sign up to request clarification or add additional context in comments.

Comments

1

You need to use the report progress option of the background worker. google this

Comments

1

Try this, very simple way of invoking an action on the Control's thread:

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

Comments

1

There should not be any issues as you are using BackgroundWorker. All the call to the callback method runs on the same UI context.

EDIT:

if you want to report progress, you need to store SynchronizationContext.Current to preferably startup. or you can Use IsInvokeRequired pattern. Here is how I use SynchronizationContext

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

I have following code , and it is working like charm.

    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;


            }

Comments

0

Only the GUI thread is allowed to modify GUI elements. If you do not respect this rule, you will get an exception. You may:

  1. use a MethodInvoker to trigger a call from within the GUI thread
  2. use the report progress feature (which does actually 1. For you)
  3. store the objects in another data structure (lock) and use a timer in the GUI thread to pull the objects and display them.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.