I want to build a cross platform GUI app for linux and windows. ETO was recommended https://github.com/picoe/Eto I'm pretty used to winforms, but I'm stuck trying to get ETO to respond to a repeated call from a timer. The examples I've found require using the winforms this.InvokeRequired, which doesn't exist under Linux.
I want a timer to start up and send a signal to update the GUI textbox every 100ms.
I have this:
// This timer needs to kick off every 100ms to update the GUI.
Timer myTimer = new Timer(TimerTick, // the callback function
new object(),
0,
100); // Every 100ms
private static void TimerTick(object state)
{
if (ProcessTics == true)
{ Task.Factory.StartNew(CallTheBackgroundFunctions); }
}
private static void CallTheBackgroundFunctions()
{
OtherClass.ProcessTic();
mainTextDisplay.Text = OtherClass.GetText(); //This errors out
}
The problem is the timer is a static instance and I've written my OtherClass as a static instance. If I provide a reference to the main form, this.Invalidate(); doesn't update the gui.
I believe I need to use ETO's invoke, but I'm really struggling to make it work.
Any suggestions for a good way to have a background thread update the GUI on an ETO form? Or suggestions for another cross platform GUI framework that supports C#? This is all for a text processing project.
Thank you for your time.
The real trick was to properly call Application.Instance.Invoke delegate.
In case anyone else runs into a similar issue, here's how I arranged the classes to update a textbox from another thread.
public static class TextRender
{
public delegate void BroadCastText(object sender, EventArgs e, string message);
public static BroadCastText Broadcasting;
public static void ProcessTic()
{
string outboundText = "";
// do some behind the scenes work.
if (Broadcasting != null) // Verify someone has subscribed to the event, otherwise null object error.
{
Broadcasting(null, EventArgs.Empty, outboundText);
}
else
{
Console.WriteLine("I'm talking but no one is listening!!");
}
}
}
public class MainForm : Form
{
Timer myTimer; // High resolution timer.
UITimer UITimerTick = new UITimer(); // Low resolution timer, but runs on the UI thread.
TextArea mainTextDisplay = new TextArea(); // Textbox that needs to be updated in realtime.
private static void TimerTick(object state)
{
TextRender.ProcessTic();
}
/// <summary>
/// Takes the incoming string and uses the UI thread to update the display.
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
/// <param name="message"></param>
private void WriteTextDelegate(Object sender, EventArgs e, string message)
{
Application.Instance.Invoke(delegate {
mainTextDisplay.Text = message;
});
}
/// <summary>
/// Toggles a timer that performs a callback to update the UI.
/// </summary>
private void Timer1()
{
if (Timer1Active)
{
TextRender.Broadcasting -= WriteTextDelegate;
myTimer.Dispose();
Timer1Active = false;
}
else
{
myTimer = new Timer(TimerTick, // the callback function
new object(),
0, // the time to wait before the timer starts it's first tick
50); // the tick interval
TextRender.Broadcasting += WriteTextDelegate; // Subscribe to the event
Timer1Active = true;
Console.WriteLine("Timer 1 start");
}
}
/// <summary>
/// Toggles using the UI timer solution. This is not intended to update faster than 1 per second.
/// </summary>
private void Timer2()
{
if (Timer2Active)
{
UITimerTick.Stop();
UITimerTick.Dispose();
Timer2Active = false;
}
else
{
UITimerTick.Interval = 0.5; //half second refresh. Can't go any faster without window freezing.
UITimerTick.Elapsed += (sender, ev) =>
{
TextRender.ProcessTic();
mainTextDisplay.Text = TextRender.CachedText;
};
UITimerTick.Start();
Timer2Active = true;
Console.WriteLine("Timer 2 start.");
}
}
}