EnLaCucha
EnLaCucha

Reputation: 476

How to handle a System.Threading Timer event in WPF

I created a Timer event handler per the code below in a class object.:

    public class MyTimerClass
    {
        private Timer _timeoutTimer;

        private void Constructor()
        {
           _timeoutTimer = new Timer(TimerHandler, null, 0, 1000);
        }

        private void TimerHandler()
        {
            if (Something) 
                LogMessage(LogLevel.Error, "Timeout Waiting...");
        }
    }

I created a LogMessageHandler event with delegate in the same class object to handle the timer event, and other log events:

    public delegate void LogMessageHandler(LogLevel logLevel, string message);
    public event LogMessageHandler OnLogMessage;

    private void LogMessage(LogLevel logLevel, string message)
    {
      if (OnLogMessage != null)
        OnLogMessage(logLevel, message);
    }

In another class, I would like to handle the log message derived for the timer. Here is my subscription to the OnLogMessage event and the class that handles the thread:

        void InitializeMyTimerClass()
        {
            try
            {
                _myTimerClass = new MyTimerClass();
                _myTimerClass.OnLogMessage += new LogMessageHandler(UpdateLogMessage);
            }
            catch (Exception ex)
            {
                _dialog.ShowException(ex.Message);
            }
        }

        private void UpdateLogMessage(LogLevel newLogLevel, string message)
        {
            TaskScheduler schedulerForLog = TaskScheduler.FromCurrentSynchronizationContext();

            Task.Factory.StartNew(() => TrackResponseMessage.Add(FormatLogLevelToString(newLogLevel) + ": " + message),
            CancellationToken.None, TaskCreationOptions.None, schedulerForLog);
        }

When I run the code and the timer event occurs, I put a break point where the TaskScheduler is created: TaskScheduler schedulerForLog = TaskScheduler.FromCurrentSynchronizationContext(); The LogLevel and string parameters are passed from MyTimerClass successfully. However, I get a InvalidOperationException when the TaskScheduler attempts to get the current synchronization context. It appears the SynchronizationContext from the timer thread is not acceptable for the TaskScheduler.

Question: Is the timer event passed in a separate thread? What is the best way to handle the timer thread in this case? Can someone provide demo code? ...Thanks!

Upvotes: 0

Views: 1291

Answers (1)

Murven
Murven

Reputation: 2387

If your problem is a cross-thread invalid operation, you can use the Application.Current.Dispatcher to invoke the process you want to run in the main thread of the application to avoid the issue:

    private void UpdateLogMessage(LogLevel newLogLevel, string message)
    {
        Application.Current.Dispatcher.BeginInvoke(new Action(()=>{
            TaskScheduler schedulerForLog = TaskScheduler.FromCurrentSynchronizationContext();

            Task.Factory.StartNew(() => TrackResponseMessage.Add(FormatLogLevelToString(newLogLevel) + ": " + message),
            CancellationToken.None, TaskCreationOptions.None, schedulerForLog);
        });
    }

If the main thread is not the thread you need then you need to get a hold of the dispatcher for the thread you want and invoke from there.

Upvotes: 2

Related Questions