-1

Is there a way for example to notify the main GUI thread of the end of the Elapsed method of a System.Timer?

My real situation is this: I have a class in business logic that uses a System.Timer to do routine operations, since the timer uses the pool thread I cannot use events that I use in the GUI because rightly these events will be invoked in a different GUI thread. What I would need is some kind of notification informing my GUI thread that the Elapsed method is finished and then I can update the GUI fields. It's possible?

A solution that I had thought of is to use the System.Windows.Forms.Timer that in its Tick method I do all the operations in an async await, but I don't like it so much because I want to leave the GUI without business logic work and I wanted to understand if there are other possible ways for this situation of mine.

aepot
  • 4,558
  • 2
  • 12
  • 24
Andre888
  • 15
  • 3
  • Maybe [this](https://stackoverflow.com/questions/24097441/best-way-to-notify-ui-whats-happening-on-another-thread-in-wpf) can point you in the right direction. – AliK Jul 18 '21 at 21:30
  • Having a Timer (in Business logic?) that works in a Thread other than the UI Thread is common. You can notify the UI Thread in different ways, depending on the implementation: You can raise an Event and leave the marshalling to a method in the UI Thread; implement `INotifyPropertyChanged`, do the marshalling in your handler then raise the PropertyChanged event: in this case you use DataBindings to update the UI; use an `IProgress` delegate provided by UI classes that need to receive the notification: in this case, the marshalling is performed by the `Progress` class itself. Etc. – Jimi Jul 18 '21 at 21:37
  • `this.Invoke((Action)(() => textBox1.Text = myString));` where `this` is `Form` or `Control`. A good alternative is `Progress` class, look for examples. Also I rdcommend to learn more about asynchronous programming. – aepot Jul 18 '21 at 21:39
  • Note that in a Winforms program, often the easiest solution is to use `System.Windows.Forms.Timer`, which automatically raises its timer event in the UI thread in the first place. – Peter Duniho Jul 18 '21 at 23:16
  • I casted a reopen vote because the [marked as duplicate](https://stackoverflow.com/questions/661561/how-do-i-update-the-gui-from-another-thread) is about the simplest way to update the GUI from another thread, while this question adds a requirement for [separation of concerns](https://en.wikipedia.org/wiki/Separation_of_concerns) between the business and the UI layer. – Theodor Zoulias Jul 19 '21 at 06:44

2 Answers2

0

You could consider notifying the UI layer from the business layer by using the IProgress<T> abstraction. The business layer class would accept a IProgress<T> implementation that would be provided by the UI layer. Any time a notification will need to be passed to the UI layer, the business layer will invoke the Report method. There is already a built-in implementation of this interface, the Progress<T> class, that marshals automatically the notifications to the UI thread, provided that the class is instantiated on the UI thread. Example:

class BusinessLayer
{
    public void Initialize (IProgress<string> progress) { /* ... */ }
}
public Form1()
{
    InitializeComponent();
    var progress = new Progress<string>(message =>
    {
        TextBox1.AppendText($"{message}\r\n");
    });
    BusinessLayer.Initialize(progress);
}

You are free to choose the type T of the IProgress<T>, according to your needs. It can be something simple like string, or a complex ValueTuple like (int, string, bool), or a custom type etc.

Something to pay attention is the frequency of the notifications. If the business layers Reports too frequently, the UI thread may get clogged and become non-responsive.

Theodor Zoulias
  • 34,835
  • 7
  • 69
  • 104
-1

Thanks a lot to everyone for the answers.

I followed the suggestion of @Teodoro, it was just what I was looking for, for my need I set the IProgress<T> like this:

public class IA_Routine
{
    IA_Core Core;
    Timer TimeRoutine;
    IProgress<object> ProgressEnd;

    public event EventHandler NotifyEndElapsedRoutine;

    public IA_Routine(IA_Core core)
    {
        Core = core;
        ProgressEnd = new Progress<object>(obj =>
        {
            FuncNotifyElapsedEnd();
        });

        TimeRoutine = new Timer();
        TimeRoutine.AutoReset = true;
        TimeRoutine.Interval = Core.TimingRoutine;
        TimeRoutine.Elapsed += TimeRoutine_Elapsed;
    }

    internal void StartRoutine()
    {
        TimeRoutine.Start();
    }

    private void TimeRoutine_Elapsed(object sender, ElapsedEventArgs e)
    {
        //-- routine functions
        //--
        //--

        ProgressEnd.Report(null);
    }

    void FuncNotifyElapsedEnd()
    {
        NotifyEndElapsedRoutine?.Invoke(this, EventArgs.Empty);
    }
}

I specify that the Timer is a System.Timers and the class instance is created in the UI thread, I don't know if it is the best way to use IProgres<T> but in the meantime it works as I wanted, thanks again

Andre888
  • 15
  • 3