Xamarin.ios - Timer not executing actions after time - c#

I am currently making a little game in Xamarin.ios and the idea is that you have 30 seconds from the time you press start until its over (and takes you to the game over screen) but if you do the wrong move then its also game over. I am using System.Timers and the timer starts fine and begins ticking but when you get to 30 seconds and it stops, it prints to the console that the timer has finished but then doesn't do anything I want it to. I have tried everything I can think of but can't seem to figure it out. Any help is appreciated, thank you! Also sorry in advance, this is my first time using stackoverflow.
Here is the main code:
//Setting up the timer
int count = 1;
System.Timers.Timer timer1;
private void OnTimeEvent(object source, ElapsedEventArgs e)
{
count++;
Console.WriteLine ("timer tick");
if (count == 30)
{
timer1.Enabled = false;
Console.WriteLine ("timer finished");
//**Everything from here on is just what I want to happen**
imgBackground.Image = UIImage.FromFile ("gameover.jpg");
btn1.SetBackgroundImage (null, UIControlState.Normal);
btn2.SetBackgroundImage (null, UIControlState.Normal);
btn3.SetBackgroundImage (null, UIControlState.Normal);
btn4.SetBackgroundImage (null, UIControlState.Normal);
lblTime.Text = "";
btnStart.Enabled = true;
hasend = true;
//score is set back to 0
score = 0;
btn1green = false;
btn2green = false;
btn3green = false;
btn4green = false;
}
}
Here is what I have on the start button (which works fine)
//Start the timer
timer1.Enabled = true;
Console.WriteLine("timer started");
And here is what I have when you make a wrong move (also works fine)
else
{
//adjust the UI
imgBackground.Image = UIImage.FromFile("gameover.jpg");
btn1.SetBackgroundImage(null, UIControlState.Normal);
btn2.SetBackgroundImage(null, UIControlState.Normal);
btn3.SetBackgroundImage(null, UIControlState.Normal);
btn4.SetBackgroundImage(null, UIControlState.Normal);
lblTime.Text = "";
btnStart.Enabled = true;
hasend = true;
//score is set back to 0
score = 0;
btn1green = false;
btn2green = false;
btn3green = false;
btn4green = false;
timer1.Enabled = false;
Console.WriteLine("timer stopped");
}
So as you can see that is a decent amount to change in the UI once the timer finishes. I think that might have something to do with why it's not working
EDIT: This is also where I set up the timer under ViewDidLoad ()
timer1 = new System.Timers.Timer();
timer1.Interval = 1000;
timer1.Elapsed += new ElapsedEventHandler(OnTimeEvent);

The problem is Timer is executed on a background thread. And background-threads can't modify UI objects.
timer1.Elapsed is set from the UI-thread in ViewDidLoad(). But it will be called from a background thread.
The solution is to wrap the code that affects UI objects in a InvokeOnMainThread (iOS) or RunOnUIThread (Android).
For an example for InvokeOnMainThread: http://developer.xamarin.com/guides/ios/user_interface/controls/part_2_-_working_with_the_ui_thread/

You can only access UI components on the UI thread. OnTimeEvent is being called from the timer's background thread not the UI thread. Another solution rather than using System.Timer and InvokeOnMainThread is to use TPL, which Xamarin supports. This will also work on android and winphone too (I think InvokeOnMainThread is ios specific?)
TaskScheduler uiContext = TaskScheduler.FromCurrentSynchronizationContext();
Console.WriteLine("timer started");
Task.Delay(30000).ContinueWith((task) =>
{
//Do UI stuff
label.Text = "!!";
Console.WriteLine("timer stopped");
}, uiContext);
Task.Delay(30000) starts and returns a background task that takes 30 seconds to complete. The ContinuesWith() call provides a method to run after that task completes, a continuation task. To make sure that continuation task runs on the UI thread a TaskScheduler is passed in. This TaskScheduler was taken from the UI thread.
Even simpler than this because you are using C# you can use the built in language support to write all that shorter:
async Task Run()
{
Console.WriteLine("timer started");
await Task.Delay(30000);
//Do UI stuff
label.Text = "!!";
Console.WriteLine("timer stopped");
}
Then just call Run();
The await keyword effectively automatically sets up a continuation task for the rest of the method and returns immediately. The rest of the method is run after the Task.Delay(30000), but the switch back to the UI context is automatically handled as await captures it.

Considering that you setup your timer in ViewDidLoad() (so you don't have any sharing code benefit in using the .Net timer) you can also use the native Timer writing just one line:
NSTimer timer = NSTimer.CreateScheduledTimer(TimeSpan.FromSeconds(30), delegate { DoYourStuffEveryTimeSpan(); });
In your case since background-threads can't modify UI objects just wrap your UI Changes with BeginInvokeOnMainThread like this:
DoYourStuffEveryTimeSpan()
{
BeginInvokeOnMainThread(() =>
{
DoMagicUIChanges();
});
}

var timer = new Timer(2000);
timer.Elapsed += OnTimerElapsed;
timer.Start ();
Console.WriteLine ("Timer started, control is back here");
void OnTimerElapsed(object sender, EventArgs e)
{
Console.WriteLine ("Time Elapsed");
}

Related

Code not executing before thread sleep?

I do this:
clear();
coinRefundComplete.Visible = true;
state = 0;
System.Threading.Thread.Sleep(4000);
clear();
greeting.Visible = true;
rate.Visible = true;
refundTicket.Visible = true;
currentTime.Visible = true;
I expect the coinRefundComplete Text (it is a label) to appear for 4 seconds, then get cleared by a method I defined with clear(), and then some other stuff happens. Instead after I clear my form with the first clear(), my form is blank for 4 seconds, then finishes properly.
Use async/await approach.
Make your method async - below example for eventhandler of button click
private async void ButtonClick(object sender, EventArgs e)
{
clear();
coinRefundComplete.Visible = true;
state = 0;
await Task.Delay(4000);
clear();
greeting.Visible = true;
rate.Visible = true;
refundTicket.Visible = true;
currentTime.Visible = true;
}
On line await Task.Delay(4000); UI thread will be release, which will update all changes were made before. After 4 seconds method will continue executing on the UI thread.
Although putting the GUI thread to sleep is never desirable but allowing the GUI thread to refresh the control state before going to Sleep will show you the changes you want. Call Control.Update or Control.Refresh after making it visible and before going to sleep so that the GUI thread is able to show changes before it goes to sleep.
clear();
coinRefundComplete.Visible = true;
label1.Update();
state = 0;
System.Threading.Thread.Sleep(4000);
clear();
You should be carefull while using Thread.Sleep, In your case it is GUI thread and GUI will be irresponsive for the time you sleep. Knowing the reason why you want to block thread could bring some other better suggestion.
Edit
You can use other thread for adding delay without blocking the GUI thread. If you can use framework 4.5 then you can use async / await construct or read this article Using async/await without .NET Framework 4.5 .
private async void testAsyncAwaitDely_Click(object sender, EventArgs e)
{
clear();
coinRefundComplete.Visible = true;
state = 0;
await Task.Delay(4000);
clear();
//Your code
}
Update [22-Apr-2018]: I've not deleted this answer so that you don't go the wrong path even though my answer is indeed a possible solution to OP's question (specially when it costs me some negative reputation as well). You should read below posts to really convince yourself as to why Application.DoEvents isn't a great solution to this problem:
Use of Application.DoEvents
Is DoEvents Evil?
Your UI is not refreshing because you are doing your entire processing on UI thread so UI thread is not getting any chance to refresh the UI elements. You need to call the Application.DoEvents() function at any place where you seek UI to be refreshed and loaded with latest changes. Here is your modified code. I've added one line of code before calling sleep on the current UI thread:
clear();
coinRefundComplete.Visible = true;
state = 0;
//new line of code
System.Windows.Forms.Application.DoEvents();
System.Threading.Thread.Sleep(4000);
clear();
greeting.Visible = true;
rate.Visible = true;
refundTicket.Visible = true;
currentTime.Visible = true;

Continuously check if condition in different thread

I have two datetimes. One is current and another is the datetime when race starts.Now I want check this Minutes difference continuously in background thread(I dont know about thread). And when it meets the if(remainingMinutes <=4) I want to update the UI. How to implement this with thread in background?
public RelayCommand OpenSetBets
{
get { return _setBets ?? (_setBets = new RelayCommand(ExecuteSetBets, CanExecuteSetBets)); }
}
private void ExecuteSetBets()
{
_navigation.NavigationToSetBetsDialogue();
}
private bool CanExecuteSetBets()
{
// Thread t = new Thread(newthread);
double? remainingMinutes = null;
if (UK_RaceDetail.Count() != 0)
{
//t.Start();
DateTime CurrentUTCtime = DateTime.UtcNow;
DateTime NextRaceTime = UK_RaceDetail[0].One.Time;
remainingMinutes = NextRaceTime.Subtract(CurrentUTCtime).TotalMinutes;
}
if (remainingMinutes <= 4)
{
return true;
}
else
{
return false;
}
}
Updated Code.
I want to enable button if race is going to start in next 4 minutes.
If you only want to use your background task to monitor the date/time, I recommend you not to create a new Thread.
For WPF, instead of thread, you can try to use DispatcherTimer object with its Tick event
System.Windows.Threading.DispatcherTimer dispatcherTimer = new System.Windows.Threading.DispatcherTimer();
dispatcherTimer.Tick += dispatcherTimer_Tick;
For WinForms, you can use Timer object with its Tick event
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
timer.Tick += timer_Tick;
This way, you are rather saved from more complex solution using Thread or ThreadPool
Edit: to use Task.Delay, as some prefer this rather "cleaner" way, see the comment by Mr. Aron
You can use Task.Run like,
System.Threading.Tasks.Task.Run(async () =>
{
//starts running in threadpool parallelly
while (!CanExecuteSetBets())
{
await Task.Delay(500); //wait 500 milliseconds after every check
}
DoYourWork(); //trigger your work here
}).ConfigureAwait(false);

Thread.Sleep() without freezing the UI

First off, I am a beginner in C# and I would like to make this:
class2.method_79(null, RoomItem_0, num, num2, 0, false, true, true);
System.Threading.Thread.Sleep(250);
class2.method_79(null, RoomItem_0, num, num4, 0, false, true, true);
System.Threading.Thread.Sleep(300);
class2.method_79(null, RoomItem_0, num, num6, 0, false, true, true);
But this solution freezes the UI, how could I make the second event occur 250ms after the first etc without freezing the UI?
The simplest way to use sleep without freezing the UI thread is to make your method asynchronous. To make your method asynchronous add the async modifier.
private void someMethod()
to
private async void someMethod()
Now you can use the await operator to perform asynchronous tasks, in your case.
await Task.Delay(milliseconds);
This makes it an asynchronous method and will run asynchronously from your UI thread.
Note that this is only supported in the Microsoft .NET framework 4.5 and higher.
.
You could use a Dispatcher Timer to time your execution of methods..
You are in the UI thread when you call .Sleep();.
That's why it's freezing the UI. If you need to do this without freezing the UI you would need to run the code in separate threads.
Run your time consuming tasks on separate thread. Avoid time consuming tasks and Thread.Sleep() on UI thread.
Try this code
public static void wait(int milliseconds)
{
System.Windows.Forms.Timer timer1 = new System.Windows.Forms.Timer();
if (milliseconds == 0 || milliseconds < 0) return;
timer1.Interval = milliseconds;
timer1.Enabled = true;
timer1.Start();
timer1.Tick += (s, e) =>
{
timer1.Enabled = false;
timer1.Stop();
};
while (timer1.Enabled)
{
Application.DoEvents();
}
}
Make an async function. Put the function in a Task.Factory.StartNew and, after that, use Thread.Sleep().
Example:
private void btnExample_Click(object sender, EventArgs e)
{
System.Threading.Tasks.Task.Factory.StartNew(() =>
{
System.Threading.Thread.Sleep(2000);
MessageBox.Show("First message after one second without freezing");
System.Threading.Thread.Sleep(2000);
MessageBox.Show("Second message after one second without freezing");
System.Threading.Thread.Sleep(2000);
MessageBox.Show("Third message after one second without freezing");
});
}

How do you use one thread to do multiple timed actions rather than multiple timers

The overlook of my project is creating an interactive tutorial. I am playing a sound clip and at certain times during the sound clip i want certain actions to take place(Ex. Highlight a button). I have come up with a solution but i feel it is eating up a lot of CPU. The solution i came up with is setting up each individual timer for each individual action that will take place during the sound clip. I foresee this crunching down on the CPU eventually if i start making say 50 different timers for 50 different actions which is making 50 different threads.
Here is a snippet of my current code using these timers;
private void Label_FindingPart_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
sound.Source = new Uri(#"C:\SomeSound.wav");
sound.Position = TimeSpan.Zero;
sound.Play();
Timer_Logistics_Button(1000);
Timer_Logistics_Window(1500);
Timer_MMBE_Button(2000);
Timer_MMBE_Window(2500);
}
//Timer initalized
private void Timer_Logistics_Button(int interval_length)
{
System.Timers.Timer aTimer = new System.Timers.Timer();
aTimer.AutoReset = false;
aTimer.Elapsed += new ElapsedEventHandler(On_Timer_Logistics_Button);
aTimer.Interval = interval_length;
aTimer.Enabled = true;
}
//Action when the Elapsed event is raised.
private void On_Timer_Logistics_Button(object source, ElapsedEventArgs e)
{
Dispatcher.BeginInvoke((Action)delegate()
{
Button_Logistics.Visibility = Visibility.Visible;
});
}
//Timer initalized
private void Timer_Logistics_Window(int interval_length)
{
System.Timers.Timer aTimer = new System.Timers.Timer();
aTimer.AutoReset = false;
aTimer.Elapsed += new ElapsedEventHandler(On_Timer_Logistics_Window);
aTimer.Interval = interval_length;
aTimer.Enabled = true;
}
//Action when the Elapsed event is raised.
private void On_Timer_Logistics_Window(object source, ElapsedEventArgs e)
{
Dispatcher.BeginInvoke((Action)delegate()
{
Image_SAP_Main.Visibility = Visibility.Hidden;
Image_SAP_Main_Logistics.Visibility = Visibility.Visible;
Button_Logistics.Visibility = Visibility.Hidden;
});
}
//Timer initalized
private void Timer_MMBE_Button(int interval_length)
{
System.Timers.Timer bTimer = new System.Timers.Timer();
bTimer.AutoReset = false;
bTimer.Elapsed += new ElapsedEventHandler(On_Timer_MMBE_Button);
bTimer.Interval = interval_length;
bTimer.Enabled = true;
}
//Action when the Elapsed event is raised.
private void On_Timer_MMBE_Button(object source, ElapsedEventArgs e)
{
Dispatcher.BeginInvoke((Action)delegate()
{
Button_MMBE.Visibility = Visibility.Visible;
});
}
//Timer initalized
private void Timer_MMBE_Window(int interval_length)
{
System.Timers.Timer bTimer = new System.Timers.Timer();
bTimer.AutoReset = false;
bTimer.Elapsed += new ElapsedEventHandler(On_Timer_MMBE_Window);
bTimer.Interval = interval_length;
bTimer.Enabled = true;
}
//Action when the Elapsed event is raised.
private void On_Timer_MMBE_Window(object source, ElapsedEventArgs e)
{
Dispatcher.BeginInvoke((Action)delegate()
{
Image_SAP_Main_Logistics.Visibility = Visibility.Hidden;
Button_MMBE.Visibility = Visibility.Hidden;
Image_SAP_MMBE.Visibility = Visibility.Visible;
});
}
My other idea is to create one thread and have it start the same time as sound.play(). This one thread would have multiple thread.sleep() statements for the wait time between each action. So in retrospect it would look like this:
Step A: Play();
Step B: Thread.Start();
Step C: Thread.sleep(1000);
Step D: ACTION;
Step E: Thread.sleep(500);
Step F: ACTION;
Step G: Thread.sleep(1200);
Step H: ACTION;
Step I: Thread.Stop();
The code below is as far as i could get with threads i was having a hard time going from step D to step E. I also still need to find out how to properly stop my thread. The code below is what i was able to fester up:
System.Threading.Thread Tutorial_Thread = new System.Threading.Thread(new System.Threading.ThreadStart(
delegate()
{
// Do the work here.
Thread.Sleep(5000);
this.Dispatcher.BeginInvoke(new Action(
delegate()
{
// Invoke any updates to the GUI here.
Button_Logistics.Visibility = Visibility.Visible;
}
));
}
));
sound.Play();
Tutorial_Thread.Start();
Once the thread is through it will go back to the start of the thread and perform that thread.sleep(5000). I am not sure how to change the sleep time and action for each rotation through the thread. Any help would be greatly appreciated and i hope i made question clear and understandable.
I am using Visual Studio 2012, C#, WPF.
A thread executes code then exits, it doesn't loop unless your code loops. In the sample you posted the thread waits for 5 seconds, changes the button's visibility, then exits.
So... since you have a defined sequence of actions, you could write it like this:
Thread Tutorial_Thread = new Thread(() =>
{
Thread.Sleep(1000);
this.Dispatcher.BeginInvoke(() => do_step_d());
Thread.Sleep(500);
this.Dispatcher.BeginInvoke(() => do_step_f());
Thread.Sleep(1200);
this.Dispatcher.BeginInvoke(() => do_step_h());
});
Tutorial_Thread.Start();
The thread will proceed through the statements, pausing at the Sleep calls, executing the various step methods, then exit.
This is a simplistic example of course. Rather than writing a static piece of code that only executes a single sequence, and writing similar code for each different sequence, you'd be better off figuring out a scheduling system that you can queue event sequences onto... possibly using mike z's suggestion to handle the timing.
Spinning off multiple things in sequence with delays between certain pieces can be addressed by using a thread and Thread.Sleep but it can be problematic as it introduces a definite wait, not to mention it ties that thread up for the duration of that wait.
Microsoft introduced the Task system as the de-facto high-level replacement of direct Thread access.
An example would be:
Play();
Task.Factory.StartNew(async () =>
{
await Task.Delay(1000);
action_1();
await Task.Delay(500);
action_2();
await Task.Delay(1200);
action_3();
});
// Task start returns immediately
Console.ReadLine();
This approach has a number of advantages over Thread.Sleep, Tasks are scheduled and the thread management is taken care of for you, tasks also have composition functions (ContinueWith etc) see the docs for more info.
The biggest advantage though is that by making the task itself an async method (using the new C# async / await), each of the times you're waiting on the delays the underlying thread can be used for other work so you're not starving the system of a thread.
I think the easiest way to do this is with async:
private async void Label_FindingPart_MouseDoubleClick(object sender, MouseButtonEventArgs e)
{
sound.Source = new Uri(#"C:\SomeSound.wav");
sound.Position = TimeSpan.Zero;
sound.Play();
await Task.Delay(1000);
Button_Logistics.Visibility = Visibility.Visible;
await Task.Delay(500);
Image_SAP_Main.Visibility = Visibility.Hidden;
Image_SAP_Main_Logistics.Visibility = Visibility.Visible;
Button_Logistics.Visibility = Visibility.Hidden;
await Task.Delay(500);
Button_MMBE.Visibility = Visibility.Visible;
await Task.Delay(500);
Image_SAP_Main_Logistics.Visibility = Visibility.Hidden;
Button_MMBE.Visibility = Visibility.Hidden;
Image_SAP_MMBE.Visibility = Visibility.Visible;
}
If you need to stop it, just use a CancellationTokenSource and pass its CancellationToken into all the Task.Delay calls.

Limit timer thread execution time

How can I restrict timer thread execution time? I have long running timer work that should work no more than 30 seconds.
The only way to do this is to have a second thread (possibly the one that created the worker thread) monitor and then kill it or gracefully call it to quit immediately. Killing threads you should avoid, and only use as the last resort. Here is example how:
Thread t = new Thread(myLongThreadProc);
t.Start();
Thread.Sleep(30000);
t.Abort();
By 'gracefully call it to quit', I mean to set some stop variable to some value, and give the thread some short time to quit itself, otherwise you kill it. But it is the design of your thread function to make it actually quit. Here is the sample code:
Thread t = new Thread(myLongThreadProc);
threadRun = true;
t.Start();
Thread.Sleep(30000);
threadRun = false; //this variable is monitored by thread
if (!t.Join(1000)) //inside your thread, make sure it does quit in one second
{ //when this variable is set to false
t.Abort();
}
And should I mention that your caller thread does not have to sleep for 30 seconds, but you can use a timer instead (if it is a form thread) or do something useful and check periodically - or have a third worker thread just counting 30 seconds...
Just have your worker method start a 30-second timer and check to see if it's elapsed as your worker does its thing:
bool timerElapsed;
public void DoWork()
{
timerElapsed=false;
System.Timers.Timer timer = new System.Timers.Timer(30000);
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_Elapsed);
timer.Start();
while (true)
{
if (timerElapsed)
{
// handle 30-sec elasped error
break;
}
// continue doing work and break when done
}
timer.Stop();
}
void timer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
timerElapsed = true;
}

Categories

Resources