How do I call a Panel function from a Timer thread to update UI elements?
I am communicating with an external web service that returns a list of data items and may be very slow (minutes to hours) to complete. When it does complete, I want to update several fields in the Panel for my C# RhinoCommon Plugin using Eto.Forms under Windows.
I’ve searched the forum and read Rhino - Event Watchers (rhino3d.com) but this issue continues to elude me. Frustratingly, while using the debugger sometimes the GUI updates will start to work, but most of the time they do not. It is unclear to me what changes to cause this intermittent behavior.
My thread is required to poll the server for results, and it is structured like this:
using Timer = System.Timers.Timer;
protected class PollTimer
{
private Int32 m_curJob;
private MyPanel m_panel = null;
public PollTimer(MyPanel panel, Int32 ms)
{
m_panel = panel;
Timer t = new Timer(ms);
t.AutoReset = true;
t.Elapsed += new ElapsedEventHandler(CheckJob);
t.Start();
}
private async void CheckJob(Object source, ElapsedEventArgs e)
{
await Task.Run(() =>
{
var result = MyPlugin.Instance.m_RemoteServer.query(m_curJob);
if (!result.completed) return;
// The Eto.Forms way to update the UI (updates all jobs)
Action action = new Action(m_panel.OnJobItemChanged);
Eto.Forms.Application.Instance.Invoke(action);
// Using InvokeOnUiThread (updates current job only)
uiOnJobComplete complete = new uiOnJobComplete(m_panel.JobIsCompleted);
RhinoApp.InvokeOnUiThread(complete, result.id);
// Using Invoke() like EventWatcher suggests, but m_Label has no .Dispatch() method
var updateJobProgress = new Action<Int32>(jobid => m_panel.JobIsCompleted(jobid));
// In this call, RhinoApp.InvokeRequired is "true" so no update is performed.
// Also, expanding 'this' in the debugger reports Eto.Forms.UIThreadAccessException
updateJobProgress.Invoke(result.id);
//updateJobProgress.BeginInvoke(result.id, null, null); // same result
RhinoApp.WriteLine("Job has completed. Terminating polling thread for job {0}", result.id);
});
}
}
The debug message prints as I expect, and when running in the debugger I can step through the individual assignment statements of the UI controls, which complete without error. However, the Rhino UI does not change (usually). I’ve most recently confirmed this with the InvokeOnUIThread() method.
However, in the process of confirming that .BeginInvoke() behaves the same as .Invoke() in the debugger, the controls are now updating, and after restarting the application .BeginInvoke() continues to work. I need to revisit this later but can anyone clarify what is happening or what the correct thing to do here is? I started this process by using .BeginInvoke() by itself and it was not working.