This is a WinForms application with a class that have several methods taking lot of time, those methods raise events at certain interval that I use to update the GUI, change labels, texts, colors, and so forth.
The Tasks run on a different thread, so they are not blocking the UI, that is fine, since I either call them from a System.Timers.Timer or create a list of Tasks and run them async.
The problem I am having is that, on the event handler I created for that class that reports me different status I cannot update the GUI, because of a cross-thread operation, so what I did to solve this is check if InvokeRequired on the EventHandler and then BeginInvoke.
That did the trick, but I'm sure this is not right, my EventHandler can be called hundred of times and each time I begin a new Invoke. What is the best approach to do?
I am not experienced with Delegates and somehow I believe I should be using them, not sure how
I leave here a sample piece of code that pretty much simplifies the workload and issue
public partial class Form1 : Form {
private Operational o = new();
private System.Timers.Timer WorkerTimer=new() { Interval=1000,Enabled=false};
public Form1() {
InitializeComponent();
o.OperationComplete += OperationCompleteEventHandler;
WorkerTimer.Elapsed += MonitorExecutor;
}
private async void MonitorExecutor(object? sender, ElapsedEventArgs e) {
List<Task> myTasks = new();
myTasks.Add(Task.Run(o.DoWork));
myTasks.Add(Task.Run(o.DoWork));
await Task.WhenAll(myTasks);
// Report all tasks have completed
}
private void OperationCompleteEventHandler(object? sender, EventArgs e) {
if (InvokeRequired) {
// Otherwise it will throw a cross-thread exception
Debug.WriteLine("Invoked Required!");
BeginInvoke(() => OperationCompleteEventHandler(sender, e));
return;
}
label1.Text = "WorkCompleted";
// But this could also take a lot of time,
// so I don't want this method to hang my thread
Thread.Sleep(500);
}
private void button1_Click(object sender, EventArgs e) {
WorkerTimer.Enabled = !WorkerTimer.Enabled;
button1.Text = WorkerTimer.Enabled ? "Running" : "Stopped";
}
}
public class Operational {
public event EventHandler? OperationComplete;
public void DoWork() {
// Long Operations
Thread.Sleep(500);
OperationComplete?.Invoke(this, new());
}
}