Search code examples
c#multithreadinglistboxbackgroundworker

C# - Listbox not updated when an entry is added to the list in a seperate thread


I have a static class that print logs to the listbox so I can use it globally, and so I used a global instance of BindingList that was defined in my cls_globalvariables static class. I bound the BindingList to the UI's listbox in the UI constructor.

public static class cls_globalvariables
{
    public static BindingList<string> logList = new BindingList<string>();
  ....
}

static class LOGS
{

    public static void LOG_PRINT(string logMessage, bool isNotError)
    {
        string now = DateTime.Now.ToString();
        try
        {
            if (logMessage == "") return;
            if (!isNotError)
                logMessage = "<<<ERROR>>>" + logMessage;

            // Output to text file.
            using (StreamWriter w = System.IO.File.AppendText(cls_globalvariables.systemlogpath))
            {
                w.WriteLine("[" + now + "][" + logMessage + "]");
                w.Close();
            }
            // Save to memory
            cls_globalvariables.logList.Add("[" + now + "][" + logMessage + "]");
        }
        catch (Exception)
        { }
    }
}

public partial class Form1: Form
{
    public Form1()
    {
        InitializeComponent();
        listBox1.DataSource = cls_globalvariables.logList;
    }
}

However, I only call LOGS.LOG_PRINT inside my backgroundworker and so it executes in another thread. The listbox delays the update until the backgroundworker finishes its process. Am I missing something?


Solution

  • You should not update the BindingList instance in a background thread because you are binding it to the main user interface thread. The user interface controls are not designed to be called from any thread other than the one that created it.

    I therefore recommend that your background thread calls back to the main thread with the new message and then the main thread adds the message to the binding list.

    Pass the SynchronizationContext.Current from the main thread into the background thread and then inside the LOGS.LOG_PRINT method you should call the Post method of the SynchronizationContext using a lambda to update the list. Something like the following...

    _syncContext.Post((_) => cls_globalvariables.logList.Add(str), null);
    

    Where str is the newly constructed logging string.