Search code examples
c#multithreadingwinformsirc

Update UI item from another class and thread


I've seen other similar questions here but I can't seem to find a solution for my specific problem.

I'm writing a Twitch Bot and need to update a listbox on the main form when a message is received from the server. I made a custom event in my TwitchBot.cs class called OnReceive that looks like this:

public delegate void Receive(string message);
public event Receive OnReceive;

private void TwitchBot_OnReceive(string message)
{
    string[] messageParts = message.Split(' ');
    if (messageParts[0] == "PING")
    {
        // writer is a StreamWriter object
        writer.WriteLine("PONG {0}", messageParts[1]);
    }
}

The event is raised in the Listen() method of my TwitchBot class:

private void Listen()
{
    //IRCConnection is a TcpClient object
    while (IRCConnection.Connected)
    {
        // reader is a StreamReader object.
        string message = reader.ReadLine();

        if (OnReceive != null)
        {
            OnReceive(message);
        }
    }
}

When connecting to the IRC back-end, I call the Listen() method from a new thread:

Thread thread = new Thread(new ThreadStart(Listen));
thread.Start();

I then subscribed to the OnReceive event in the main form using the following line:

// bot is an instance of my TwitchBot class
bot.OnReceive += new TwitchBot.Receive(UpdateChat);

Finally, UpdateChat() is a method in the main form used to update a listbox on it:

private void UpdateChat(string message)
{
    lstChat.Items.Insert(lstChat.Items.Count, message);
    lstChat.SelectedIndex = lstChat.Items.Count - 1;
    lstChat.Refresh();
}

When I connect to the server and the Listen() method runs, I get an InvalidOperationException that says "Additional information: Cross-thread operation not valid: Control 'lstChat' accessed from a thread other than the thread it was created on."

I've looked up how to update UI from a different thread but can only find things for WPF and I'm using winforms.


Solution

  • You should check the Invoke for UI thread

    private void UpdateChat(string message)
    {
        if(this.InvokeRequired)
        {
            this.Invoke(new MethodInvoker(delegate {
                lstChat.Items.Insert(lstChat.Items.Count, message);
                lstChat.SelectedIndex = lstChat.Items.Count - 1;
                lstCat.Refresh();
            }));           
        } else {
                lstChat.Items.Insert(lstChat.Items.Count, message);
                lstChat.SelectedIndex = lstChat.Items.Count - 1;
                lstCat.Refresh();
        }
    }