DispatcherTimer keeps firing after I've stopped it. C# UWP - c#

I'm having trouble with the following code. I have some code that calls SetTimer() and expects the user to respond before interval is reached (in
millisecs). The calling code inherit these funtions. If the user responds, then StopTimer() is called, info is displayed, StartTimer() is called, and the user is expected to respond again within the interval time period. This continues until the user fails in an answer or takes too long (goes past the interval).
The problem is the timers don't stop. They keep repeating even after I've stopped them, set their Tick event to null (by the -= method), and left its scope. I even get new storage with a new DispatcherTimer (I've done this both using the old one and a new one each time). I can't get the old Timer to go away.
What am I doing wrong?
using Windows.UI.XAML;
public DispatcherTimer GameTimer;
internal void SetTimer(int interval)
{
GameTimer = new DispatcherTimer();
GameTimer.Tick += TimerCallback;
GameTimer.Interval = new TimeSpan(0,0,0,0,interval);
GameTimer.Start();
}
internal void StopTimer()
{
GameTimer.Stop();
try
{
GameTimer.Tick -= TimerCallback;
} catch {}
}
private void TimerCallback(object sender, object e)
{
StopTimer();
// Other code
}
Thanks in advance,
-justin

Try stopping the timer by using the sender object, not the actual public timer object:
private void TimerCallback(object sender, object e) {
(sender as DispatcherTimer).Stop();
// Other code
}

As a workaround, you could do something like:
// in your class
private bool _allowExecution = false;
Then whenever you start the time set _allowExecution = true; and when you stop the timer, simply add _allowExecution = false;
The last thing will be to add a simply boolean condition on your timer execute: if (_allowExecute) //do your stuff here

Because you initialize a new DispatcherTimer everytime call SetTimer(int interval). You must stop the old DispatcherTimer instance before initialize a new one.
internal void SetTimer(int interval)
{
StopTimer();
GameTimer = new DispatcherTimer();
GameTimer.Tick += TimerCallback;
GameTimer.Interval = new TimeSpan(0,0,0,0,interval);
GameTimer.Start();
}
internal void StopTimer()
{
if(GameTimer != null)
{
GameTimer.Stop();
GameTimer.Tick -= TimerCallback;
GameTimer = null;
}
}

Related

Is it possible to get Timer.Tick event after diposing it

I have a question about System.Windows.Forms.Timer. Is it possible to get Tick event after disposing it? For example, if the message is in the message loop and I dispose the timer meanwhile. If it is possible what is the best way to prevent against it. Do you now any good sources explaining it, because I couldn't find anything explaining it. Here is same code explaining my problem:
namespace TestTimer
{
public partial class Form1 : Form
{
ObjectWithTimer obj = null;
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
if(obj != null)
{
obj.Deinit();
obj = null;
}
obj = new ObjectWithTimer();
}
}
public class ObjectWithTimer
{
public Object o = new object();
public Timer timer = new Timer();
bool disposed = false;
public ObjectWithTimer()
{
timer.Interval = 10;
timer.Tick += new EventHandler(timer_Tick);
timer.Enabled = true;
}
public void Deinit()
{
timer.Enabled = false;
timer.Tick -= new EventHandler(timer_Tick);
timer.Dispose();
timer = null;
disposed = true;
}
private void timer_Tick(object sender, EventArgs e)
{
if (disposed)
{
//Is it possible to get here
if (timer.Enabled) return;
}
//doing something
}
}
}
Understanding how timers work can help you feel better about it. They are implemented by the operating system, the underlying winapi call to start a timer is SetTimer(). The OS then posts a notification whenever the timer ticks, you get a WM_TIMER message. The plumbing in Winforms ensures that your Tick event handler runs when this message is received.
These messages are stored in the message queue, an internal data structure associated with a window. This queue serializes messages, it is the basic mechanism that ensures that you for example can never lose a mouse click or a keyboard key press, even when the window is unresponsive because the UI thread is busy with something else.
This queue gives reason to be cautious, what happens when the queue stores a WM_TIMER message when you disposed the timer? Unless something drastic is done, you'd still get that message and your Tick event handler will fire.
But no need to worry, WM_TIMER belongs to a small group of messages that are generated in a special way. They are synthesized messages, it is only ever generated when your program asks for a message with GetMessage(). Other common messages that belong that group are WM_PAINT, it fires the Paint event. Note how you can call Invalidate() as often as you like, you still get only a single Paint event. WM_MOUSEMOVE is another one, it fires the MouseMove event. Something you can reason about, no matter how fast you move the mouse, you can never flood the message queue with mouse-move messages.
Another characteristic of these synthesized messages is that they appear to have a "low priority". Given is that they are only ever synthesized when the message queue is empty. Which is why keyboard messages and mouse clicks always generate an event ahead of a paint.
Long story short, you can only get a WM_TIMER message if you ask for a message and the timer is still alive. The Timer.Dispose() method calls KillTimer() under the hood. Which ends any opportunity to still get a Tick event. Only possible way that could get screwed up is when you call the Stop() or Dispose() methods from a worker thread. Don't do that.
The Windows Forms Timer is single threaded so is not possible that while disposing it you are in timer_Tick.
Also you are not detaching your event in deinit function.
This is very easy to test. I've modified your code a bit:
public class Form1 : Form
{
public Form1()
{
var button = new Button();
button.Click += button1_Click;
Controls.Add(button);
}
private void button1_Click(object sender, EventArgs e)
{
var obj = new ObjectWithTimer();
Thread.Sleep(2000);
obj.Deinit();
}
}
public class ObjectWithTimer
{
public System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
bool disposed = false;
public ObjectWithTimer()
{
timer.Interval = 100;
timer.Tick += new EventHandler(timer_Tick);
timer.Enabled = true;
}
public void Deinit()
{
timer.Enabled = false;
timer.Tick -= new EventHandler(timer_Tick);
timer.Dispose();
timer = null;
disposed = true;
}
private void timer_Tick(object sender, EventArgs e)
{
"Ticked".Dump();
}
}
The Thread.Sleep ensures the UI thread is occupied while the timer does its ticking.
The result? No, the Tick will not fire after the timer is disabled. Even the timer.Tick -= new EventHandler(timer_Tick); is unnecessary.

backgroundworker starts more than once

I have a issue that I really dont know why it occurs at all. I wpf c# application that use a timer to start a backgroundworker, sometimes the backgroundworker start the task twice, and I don't know why. The code I use is this....
private void startScheduledTask()
{
// Timer settings and start
dpTimer.Interval = TimeSpan.FromMilliseconds(CalculateTimerInterval(CHECK_INTERVAL));
dpTimer.Tick += new EventHandler(StartScheduledActivity);
dpTimer.Start();
}
private void StartScheduledActivity(Object sender, EventArgs args)
{
// Timer tick has occured, start scheduled work
StartScheduledWork();
dpTimer.Interval = TimeSpan.FromMilliseconds(CalculateTimerInterval(CHECK_INTERVAL));
}
private void StartScheduledWork()
{
MyHeavyWorker = new System.ComponentModel.BackgroundWorker();
if ((!MyHeavyWorker.IsBusy) && (MyHeavyWorker != null))
{
MyHeavyWorker.WorkerReportsProgress = true;
MyHeavyWorker.WorkerSupportsCancellation = true;
MyHeavyWorker.ProgressChanged += MyHeavyWorker_ProgressChanged;
MyHeavyWorker.DoWork += MyHeavyWorker_DoWork;
MyHeavyWorker.RunWorkerCompleted += MyHeavyWorker_RunWorkerCompleted;
MyHeavyWorker.RunWorkerAsync();
}
}
private void MyHeavyWorker_DoWork(object sender, System.ComponentModel.DoWorkEventArgs e)
{
// This method sometime run twice at a time
FetchSomeFiles();
}
public int CalculateTimerInterval(int minute)
{
if (minute <= 0)
{
minute = 60;
}
DateTime CurrTime = DateTime.Now;
DateTime now = DateTime.Now;
DateTime future = now.AddMinutes((minute - (now.Minute % minute))).AddSeconds(now.Second * -1).AddMilliseconds(now.Millisecond * -1);
TimeSpan interval = future - now;
NextExecutionTime = future.ToShortTimeString();
NextExecutionDateTime = NextExecutionTime.ToString();
return Convert.ToInt32(interval.TotalMilliseconds);
}
Can anyone see why the method FetchSomeFiles sometimes runs twice at the same time?
It's quite simply because you are each time initializing a new instance of your backgroundworker - so if your timer event occurs before the previous backgroundworker is done it will start a second time with another bg Worker instance. Keep your Backgroundworker reference on class level and initialize it only once.
Do the same thing with the eventhandlers you are adding - move them to the class constructor or to a method called once when your object is instanciated.
//Put this line on class level and only initialize it once.
MyHeavyWorker = new System.ComponentModel.BackgroundWorker();
//Call this once to initialize your Backgroundworker
public void InitializeBackgroundWorker()
{
MyHeavyWorker.WorkerReportsProgress = true;
MyHeavyWorker.WorkerSupportsCancellation = true;
MyHeavyWorker.ProgressChanged += MyHeavyWorker_ProgressChanged;
MyHeavyWorker.DoWork += MyHeavyWorker_DoWork;
MyHeavyWorker.RunWorkerCompleted += MyHeavyWorker_RunWorkerCompleted;
}
Then check for the MyHeavyWorker.IsBusy of your one and only instance to check if it is currently doing some work before deciding to call RunWorkerAsync().
Another method would also be to just stop your timer with dpTimer.Stop() in StartScheduledActivity before you launch your BackgroundWorker and call dpTimer.Start() again in MyHeavyWorker_RunWorkerCompleted. Of course you will have to reconsider how you would like to calculate your next interval since with this solution the countdown does start after your backgroundworker is done - which could be considerably later than the point of the start.
Check
if MyHeavyWorker.IsBusy
before starting the task inside the DoWork() Method. This method will check if DoWork() is still running and will not start another call of this method until it is finished

System.Windows.Forms.Timer - NullReferenceException

Is there any explanation to NullReference exception, that occured at one machine today. I cannot reproduce it at my computer....
class Test
{
Timer timer_;
public void Init()
{
timer_ = new Timer();
timer_.Interval = 10000;
timer_.Tick += OnTimerTick;
timer_.Start();
}
private void OnTimerTick(object sender, EventArgs e)
{
timer_.Stop();
timer_ = null; <--- Null Ref occurs
}
}
Solution based on awesome advices of Mark Hall and Rich Okelly
private void OnTimerTick(object sender, EventArgs e)
{
var localTimer = Interlocked.Exchange(ref timer_, null);
if (localTimer != null)
{
localTimer.Stop();
localTimer.Tick -= OnTimerTick;
localTimer.Dispose();
// doing staff
}
}
I think the null reference exception actually occurs the line above: at timer_.Stop().
What happened was the Tick event was raised and another scheduled, the timer was stopped and set to null as a result of the first Tick event. The second Tick event then tries to call Stop on the Timer, which is now null.
You can use the Interlocked methods to work around this:
private void OnTimerTick(object sender, EventArgs e)
{
var localTimer= Interlocked.Exchange(ref timer_, null);
if (localTimer != null)
{
localTimer.Stop();
}
}
Try removing your OnTimerTick event before setting your timer to null. That will prevent it from being raised while you are setting it to null, but since you are creating a 10 sec one-shot that is unlikely, try disposing of your timer before setting it to null;
i.e.
private void OnTimerTick(object sender, EventArgs e)
{
timer_.Stop();
timer_.Tick -= OnTimerTick;
timer_.Dispose();
timer_ = null;
}
You said you are using System.Windows.Forms.Timer, the documentation says :
This
Windows timer is designed for a single-threaded environment where UI
threads are used to perform processing.It requires that the user code
have a UI message pump available and always operate from the same
thread, or marshal the call onto another thread.
So using Interlocked.Exchange isn't needed, this is not a concurrency issue.
You can try the following code :
public void Init()
{
if (timer_ != null)
throw new InvalidOperationException("Already initialized!");
timer_ = new Timer();
timer_.Interval = 10000;
timer_.Tick += OnTimerTick;
timer_.Start();
}
private void OnTimerTick(object sender, EventArgs e)
{
if (timer_ != null)
{
timer_.Stop();
timer_.Dispose();
timer_ = null;
// Your code
}
}
This way, on the first tick timer_ will be stopped and set to null. If there is any pending Tick, it will be ignored thanks to (timer_ != null).
Moreover if Init() is called while the timer is running (probably a bug) you will see it soon.

How do I show text on a label for a specific time (like 3 seconds)?

I have a statusbar label and I want to show a text on my StatusBar Label for 3 seconds only
How can I do it without using threads?
public void InfoLabel(string value)
{
if (InvokeRequired)
{
this.Invoke(new Action<string>(InfoLabel), new object[] { value });
return;
}
infoLabel.Text = value;
}
Simply add timer on the end of your method:
if (!string.IsNullOrWhiteSpace(value))
{
System.Timers.Timer timer = new System.Timers.Timer(3000) { Enabled = true };
timer.Elapsed += (sender, args) =>
{
this.InfoLabel(string.Empty);
timer.Dispose();
};
}
You need to define a function that you call each time you need to display your text, inside this function you define a timer, this timer is based on System.Windows.Forms.Timer, the only difference is that its modified to hold a stopTime parameter that represents the running duration, the only thing you need to do is to put your starting code(display text) inside the MyFunction function and to put the ending code(to stop displaying text) inside the Timer_Tick function, once you call MyFunction just specify how many seconds you want it to run in the function parameter.
private void MyFunction(int durationInSeconds)
{
MyTimer timer = new MyTimer();
timer.Tick += new EventHandler(Timer_Tick);
timer.Interval = (1000) * (1); // Timer will tick every second, you can change it if you want
timer.Enabled = true;
timer.stopTime = System.DateTime.Now.AddSeconds(durationInSeconds);
timer.Start();
//put your starting code here
}
private void Timer_Tick(object sender, EventArgs e)
{
MyTimer timer = (MyTimer)sender;
if (System.DateTime.Now >= timer.stopTime)
{
timer.Stop();
//put your ending code here
}
}
the modified timer class
public class MyTimer : System.Windows.Forms.Timer
{
public System.DateTime stopTime;
public MyTimer()
{
}
}
You can use Timer to create an instance of a timer that waits for n seconds before firing the Elapsed event. In the elapsed event, you clear the label's Content.
As the timer is executed in a separate thread, the UI thread is not locked while the timer is counting i.e. you are free to perform other operations in the UI.
private delegate void NoArgDelegate();
private void StartTimer(int durationInSeconds)
{
const int milliSecondsPerSecond = 1000;
var timer = new Timer(durationInSeconds * milliSecondsPerSecond);
timer.Start();
timer.Elapsed += timer_Elapsed;
}
private void timer_Elapsed(object sender, ElapsedEventArgs e)
{
var clearLabelTextDelegate = new NoArgDelegate(ClearLabelText);
this.Dispatcher.BeginInvoke(clearLabelTextDelegate);
}
private void ClearLabelText()
{
this.myLabel.Content = string.Empty;
}
As I do not the rest of your code, some suggestions would be to create a lock on the timer so as to prevent more than one UI event starting the timer. In addition, the delegate and the timer instance can be made as private members of the class.
You'll always be using at least the GUI thread. If you decide to wait on that thread, no other interaction with controls is possible (ie. no buttons will work, the window will not be repainted).
Alternatively you could use a System.Windows.Forms.Timer that gives control back to the OS, or another type of timer. Either way, the "countdown" will either block user interaction or happen on another thread (under the hood).

C# timer (slowing down a loop)

I would like to slow down a loop so that it loops every 5 seconds.
In ActionScript, I would use a timer and a timer complete event to do this. How would I go about it in C#?
You can add this call inside your loop:
System.Threading.Thread.Sleep(5000); // 5,000 ms
or preferable for better readability:
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
However, if your application has a user interface you should never sleep on the foreground thread (the thread that processes the applications message loop).
You can try using Timer,
using System;
public class PortChat
{
public static System.Timers.Timer _timer;
public static void Main()
{
_timer = new System.Timers.Timer();
_timer.Interval = 5000;
_timer.Elapsed += new System.Timers.ElapsedEventHandler(_timer_Elapsed);
_timer.Enabled = true;
Console.ReadKey();
}
static void _timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
//Do Your loop
}
}
Also if your operation in loop can last more then 5 sec, You can set
_timer.AutoReset = false;
to disable next timer tick until operation finish in loop
But then end end of loop You need again to enable timer like
_timer.Enabled = true;
Don't use a loop at all. Set up a Timer object and react to its fired event. Watch out, because these events will fire on a different thread (the timer's thread from the threadpool).
Let's say you have a for-loop that you want to use for writing to a database every second. I would then create a timer that is set to a 1000 ms interval and then use the timer the same way you would use a while-loop if you want it to act like a for-loop. By creating the integer before the loop and adding to it inside it.
public patial class Form1 : From
{
timer1.Start();
int i = 0;
int howeverLongYouWantTheLoopToLast = 10;
private void timer1_Tick(object sender, EventArgs e)
{
if (i < howeverLongYouWantTheLoopToLast)
{
writeQueryMethodThatIAssumeYouHave(APathMaybe, i); // <-- Just an example, write whatever you want to loop to do here.
i++;
}
else
{
timer1.Stop();
//Maybe add a little message here telling the user the write is done.
}
}
}

Categories