Reputation: 11754
I have that code where my action is never executed:
private static Timer timer = new Timer(TimerCallback, null, 5000, 5000);
private static DispatcherOperation dispatcherOperationPrevious = null;
private static void TimerCallback(object state)
{
DispatcherOperation dispatcherOperation =
Dispatcher.CurrentDispatcher.BeginInvoke(new Action(() =>
{
Debug.Print("Pass here at: " + DateTime.Now.ToString("O"));
}));
dispatcherOperationPrevious = dispatcherOperation;
}
I have no debug traces about "Pass here...".
If I look at the result of BeginInvoke which is dispatcherOperation, I get (either for current or previous dispatcherOperation):
dispatcherOperation.Status = Pending
dispatcherOperation.Task.Status = WaitingForActivation
_dispatcherOperationPrevious .Status = Pending
_dispatcherOperationPrevious .Task.Status = WaitingForActivation
Why "Debug.Print..." is never executed?
Upvotes: 1
Views: 1915
Reputation: 17402
As stated in this post, Dispatcher.CurrentDispatcher
will get the Dispatcher
from the current thread, which in your case, is the thread of the Timer
. If you want to invoke your debug call from the GUI thread, you need to use Application.CurrentDispatcher
instead.
private static void TimerCallback(object state)
{
DispatcherOperation dispatcherOperation =
Application.CurrentDispatcher.BeginInvoke(new Action(() =>
{
Debug.Print("Pass here at: " + DateTime.Now.ToString("O"));
}));
dispatcherOperationPrevious = dispatcherOperation;
}
Though, instead of using a standard Timer
, I would recommend using a DispatcherTimer
. The DispatcherTimer
is designed to be used when updating the UI (no need to explicitly invoke).
If a System.Timers.Timer is used in a WPF application, it is worth noting that the System.Timers.Timer runs on a different thread then the user interface (UI) thread. In order to access objects on the user interface (UI) thread, it is necessary to post the operation onto the Dispatcher of the user interface (UI) thread using Invoke or BeginInvoke. Reasons for using a DispatcherTimer opposed to a System.Timers.Timer are that the DispatcherTimer runs on the same thread as the Dispatcher and a DispatcherPriority can be set on the DispatcherTimer.
Upvotes: 1
Reputation: 22038
This is because that the TimeCallback is called on another thread. When you call Dispatcher.CurrentDispatcher on a thread that doesn't have a Dispatcher, it will create a new one. (A Dispatcher is bound to a thread.)
So, you're creating a new dispatcher, but this new dispatcher is never Run()
and your Action will be queued on a non running dispatcher.
I think you want to call the Action on the GUI dispatcher.
Every WPF control is dirived from DispatcherObject
and will contain a property Dispatcher
containing the dispatcher on where they are made on. Use that one.
So Dispatcher.Invoke
could be interpreted as a static method call, but it is actually the a method call of the Dispatcher instance stored in the controls property Dispatcher.
Fix:
private static Timer timer = new Timer(TimerCallback, null, 5000, 5000);
private static DispatcherOperation dispatcherOperationPrevious = null;
private static void TimerCallback(object state)
{
DispatcherOperation dispatcherOperation =
// Dispatcher here is using the controls dispatcher, the BeginInvoke is not a static method.. (like you probably assumed..
// So calling the BeginInvoke of the controls property 'Dispatcher'
Dispatcher.BeginInvoke(new Action(() =>
{
Debug.Print("Pass here at: " + DateTime.Now.ToString("O"));
}));
dispatcherOperationPrevious = dispatcherOperation;
}
If the Dispatcher property is not avaiable within the TimeCallback, for example if you created the time within some classes deep in you business layer (bad practice), you can call Application.Current.Dispatcher
to get the 'main/gui' Dispatcher.
Upvotes: 1