I'm making my way through threading design patterns and am stuck trying to resolve a small practice project. I get a [InvalidOperationException: 'Cross-thread operation not valid: Control 'txtEventLogger' accessed from a thread other than the thread it was created on.'] when an event in my UI triggered by my object in another thread, calls a method in my UI from the UI event handler, to log some events in a textbox. How can I make this thread safe? Or am I using the wrong design pattern for handling object events when it comes to using threads?
My GUI WinForm has 3 TextBoxes and 1 button:
- txtAmountOfMessages
- txtMessageToSend
- txtEventLogger
- btnStart
I have a class named Relay, and another class derived of EventArgs CustomEventArgs for event handling.
public class Relay
{
//CustomEventArgs, derived EventArgs class to hold object index and a string message
public delegate void OnSend(object sender, CustomEventArgs e);
public event OnSend Sent;
public int index;
public int Index {get => index; set => index = value; }
public void Send(string UserMessage)
{
//TODO: Meanwhile trigger event for testing
//EventArgs class not shown, but it takes:
//CustomEventArgs Constructor CustomEventArgs(int index, string message)
Sent(this, new CustomEventArgs(this.index, UserMassage.Length.ToString()));
}
}
My WinForm code:
public partial class Form1 : Form
{
private void btnStart_Click(object sender, EventArgs e)
{
// create a couple threads
for (int i = 1; i < 3; i++)
{ // txtAmountOfMessages = 3
new Thread(() => CreateRelaysAndSendMessage(Convert.ToInt32(txtAmountOfMessages.Text), txtMessageToSend.Text)).Start();
}
}
private void CreateRelaysAndSendMessage(int AmountOfMessages, string Message)
{
List<Relay> relayList = new List<Relay>();
// Create 5 objects of Relay class, index, and subscribe to ui eventhandler
for (int i = 0; i <= 4; i++)
{
relayList.Add(new Relay());
relayList[i].Index = i;
relayList[i].Sent += RelaySent;
}
// For each object, call .Send, 3 times (from txtAmountOfMessages value)
foreach(Relay myRelay in relayList)
{
for (int j = 1; j <= AmountOfMessages; j++)
{
myRelay.Send(Message);
}
}
}
private void RelaySent(object sender, CustomEventArgs e)
{
// Exception handling error here
Log("Relay number " + e.Index.ToString() + " sent msg " + e.Message);
}
public void Log(string Message)
{
// Exception handling error here
//System.InvalidOperationException: 'Cross-thread operation not valid: Control 'txtEventLogger' accessed from a thread other than the thread it was created on.'
txtEventLogger.AppendText(Message + Environment.NewLine);
}
}