Xamarin StartTimer subtract seconds from TimeSpan don't work - c#

I am programming a countdown timer with Xamarin Forms using the StartTimer method of Device class. The problem is that the selected time is not reduced by seconds but remains as originally selected. I don't know where the problem is. Thanks for the help.
public partial class MainPage : ContentPage
{
private int minutes = 0;
private int hours = 0;
private TimeSpan restTime = TimeSpan.Zero;
public MainPage()
{
InitializeComponent();
StartCountdown(hours, minutes);
}
public void StartCountdown(int hours, int min)
{
var M = TimeSpan.FromMinutes(min);
var H = TimeSpan.FromHours(hours);
restTime = H.Add(M);
Device.StartTimer(TimeSpan.FromSeconds(1), () =>
{
//Here should subtract from variable restTime one second every second
restTime.Add(new TimeSpan(0,0,-1));
if (restTime == TimeSpan.Zero)
{
//Do something
return false;
}
else
{
return true;
}
});
}

You're calling restTime.Add, but not doing anything with the result, here:
restTime.Add(new TimeSpan(0,0,-1));
The Add method doesn't change the existing TimeSpan - it returns a new one. Instead, you want:
restTime = restTime.Add(new TimeSpan(0, 0, -1));
Or more readably IMO:
restTime -= TimeSpan.FromSeconds(1);
(I'd also use restTime = new TimeSpan(hours, min, 0); to start with, probably. Or maybe restTime = TimeSpan.FromHours(hours) + TimeSpan.FromMinutes(min);.)
You might want to have a field for one second:
private static readonly TimeSpan OneSecond = TimeSpan.FromSeconds(1);
...
restTime -= OneSecond;

Related

Check if the countdown timer is 0

I have a script based on the countdown timer. I want that when the time reaches 0, the timer stop and a message appear. The code id this:
public partial class simulare : Form
{
private admin admin;
Timer timer = new Timer();
public simulare(admin admin)
{
InitializeComponent();
this.admin=admin;
label2.Text = TimeSpan.FromMinutes(0.1).ToString();
}
private void simulare_Load(object sender, EventArgs e)
{
var startTime = DateTime.Now;
timer = new Timer() { Interval = 1000 };
timer.Tick += (obj, args) =>
label2.Text = (TimeSpan.FromMinutes(0.1) - (DateTime.Now - startTime)).ToString("hh\\:mm\\:ss");
timer.Enabled = true;
timer.Start();
if (condition)
{
timer.Stop();
MessageBox.Show("Done!");
}
}
}
I tried those conditions, but unsuccessful:
if (timer.ToString() == TimeSpan.Zero.ToString())
if (label2.Text.ToString() == TimeSpan.Zero.ToString())
if (label2.Text == TimeSpan.Zero)
You could extract the calculation and assign the result to a TimeSpan variable, then check if the Seconds in that TimeSpan variable are equals to zero
void simulare_Load(object sender, EventArgs e)
{
var startTime = DateTime.Now;
timer = new System.Windows.Forms.Timer() { Interval = 1000 };
timer.Tick += (obj, args) =>
{
TimeSpan ts = TimeSpan.FromMinutes(0.1) - (DateTime.Now - startTime);
label1.Text = ts.ToString("hh\\:mm\\:ss");
if (ts.Seconds == 0)
{
timer.Stop();
MessageBox.Show("Done!");
}
};
timer.Start();
}
First off, checking anything in the Load event isn't going to work. That code only runs once (on form load).
So you need a more complex tick event, which I would put into an actual function instead of a lambda:
private int countDown = 50; //Or initialize at load time, or whatever
public void TimerTick(...)
{
label2.Text = (TimeSpan.FromMinutes(0.1) - (DateTime.Now - startTime)).ToString("hh\\:mm\\:ss");
countDown--;
if (countDown <= 0)
timer.Stop();
}
I use an int counter here since checking against a view property (the text in this case) isn't a very good design/practice. If you really want a TimeSpan, I would still save it off instead of checking directly against the Text property or a string.

Making my program pause between drawing cards

I have made a little Blackjack game, and I'd like to make the computer wait between each card he pulls, however using System.Threading.Thread.Sleep(int x) does not make the program wait between cards, but makes it wait for x * amount of cards..
I also know that using Thread.Sleep is not a good way, so I'd rather learn a better way as I am creating this program entirely for educative purposes.
I'll add the code underneath which decides whether or not a card should be drawn, and the method that draws the card.
private void ComputerTurn()
{
drawCard.Enabled = false;
finishTurn.Enabled = false;
while (computerTotalScore <= 11)
{
ComputerDrawCard();
}
drawAgain = true;
while (drawAgain)
{
ComputerDrawCard();
if (totalScore <= 21)
{
if (computerTotalScore > totalScore)
{
drawAgain = false;
}
else
{
drawAgain = true;
}
}
else
{
if (computerTotalScore > 16)
{
drawAgain = false;
}
else
{
drawAgain = true;
}
}
}
DecideWinner();
}
public void ComputerDrawCard()
{
cardAlreadyPulled = true;
while (cardAlreadyPulled)
{
cardType = random.Next(0, 4);
cardNumber = random.Next(0, 13);
if (!pulledCards[cardType, cardNumber])
{
cardAlreadyPulled = false;
pulledCards[cardType, cardNumber] = true;
}
}
ComputerUpdateCardPictures();
computerScore = cardScores[cardNumber];
if (computerScore == 1)
{
if (computerTotalScore <= 10)
{
computerScore = 11;
}
else
{
computerScore = 1;
}
}
computerTotalScore += computerScore;
txtComputerCurrentScore.Text = computerScore.ToString();
txtComputerTotalScore.Text = computerTotalScore.ToString();
System.Threading.Thread.Sleep(random.Next(250, 750));
}
There are multiple ways to achieve something like this. I believe what you're attempting to do is simulate a human taking time to do things. I recommend using a combination of expected wait times and a timer to achieve what you want.
class Example
{
public Example()
{
// create a stalled timer
_pulse = new Timer(this.TakeAction);
}
TimeSpan _drawdelay = TimeSpan.FromSeconds(2);
DateTime _lastAction = DateTime.MinValue;
Timer _pulse;
public void Start()
{
// start the timer by asking it to call the worker method ever 0.5 seconds
_pulse.Change(0, 500);
}
public void Stop()
{
// stop the timer by setting the next pulse to infinitely in the future
_pulse.Change(Timeout.Infinite, Timeout.Infinite);
}
void TakeAction(object x)
{
lock (_pulse)
{
DateTime now = DateTime.Now;
if(now - _lastAction > _drawdelay)
{
// do work ...
_lastAction = now;
}
}
}
}
That said, the above code will run into issues if the work being done takes longer than 500 milliseconds to complete. Add thread safety as necessary.
I would add a last time drawn and time between draws members. Then before drawing a card, get the time between now and the last time pulled. If the time is greater than the allowed time between the draws its cool to draw.
private DateTime _lastWrite = DateTime.Now;
private TimeSpan _delay = TimeSpan.FromMilliseconds(100);
public void ComputerDrawCard() {
var now = DateTime.Now;
if (now - _lastWrite < _delay)
return;
_lastWrite = now;
draw card...
}
Here's a gist of an example working correctly.

DispatcherTimer and Button c# conflict

I'm very new to WP8 dev and c#. I'm trying to make a loop that counts up by n on an interval. I want to press a button to increment n.
Here is my code right now:
namespace Petsounds {
public partial class MainPage : PhoneApplicationPage {
float clicks = 0;
float clickers = 0;
float clickerBuyers = 0;
float clickerCost = 5;
float clickerBuyerCost = 500;
long savedTime = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;
bool buyClickerButtonFlag = false;
bool clickButtonFlag = false;
// Constructor
public MainPage() {
InitializeComponent();
//
DispatcherTimer t = new DispatcherTimer();
t.Interval = TimeSpan.FromMilliseconds(10);
t.Tick += (s, e) => startLoop();
t.Start();
}
private void clickButtonOnClick(object sender, RoutedEventArgs e) {
clickButtonFlag = true;
System.Diagnostics.Debug.WriteLine("clicked!" + clicks);
}
private void buyClickerButtonOnClick(object sender, RoutedEventArgs e) {
buyClickerButtonFlag = true;
}
private void startLoop() {
if (true) {
long nowTime = savedTime;
long timePassed = nowTime - savedTime;
//user input
if (clickButtonFlag) {
clickButtonFlag = false;
clicks++;
System.Diagnostics.Debug.WriteLine("clicked!" + clicks);
}
if (buyClickerButtonFlag) {
buyClickerButtonFlag = false;
if (clicks > clickerCost) {
clickers++;
clicks -= clickerCost;
clickerCost *= 1.6F;
}
System.Diagnostics.Debug.WriteLine("clicker bought!" + clickers);
}
//update vars
if (timePassed > TimeSpan.TicksPerSecond) {
savedTime = nowTime;
nowTime = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;
clicks += clickers;
}
//update display
clickCount.Text = clicks.ToString();
buyClickerButtonCost.Text = "Cossst " + clickerCost.ToString();
}
}
}
}
My button's are inconsistent, and if I remove the thread, the buttons are responsive (but of course the counter doesn't work.)
EDIT:
I've changed
DispatcherTimer t = new DispatcherTimer();
t.Interval = TimeSpan.FromMilliseconds(10);
t.Tick += (s, e) => startLoop();
t.Start();
to
Timer myTimer = new Timer(startLoop);
myTimer.Change(1000, 10);
And now get an error:
A first chance exception of type 'System.UnauthorizedAccessException' occurred in System.Windows.ni.dll
on line
clickCount.Text = clicks.ToString();
First of all... you will quickly find that 10ms is not really 10ms... It might not even be that close... If you did 1000ms... that would be expected to be more accurate.
Also, a DispatcherTimer is going to queue up a function call to the GUI thread each interval... which means you are flooding the GUI thread with startLoop() calls. This doesn't give the thread much time to update anything else... like your buttons.
There is a different approach you might want to consider.
If your task is to increment a numeric value when a user touches a button (and have the numbers increase at a steady pace) consider using the RepeatButton.
RepeatButton: Represents a control that raises its Click event repeatedly from the time it is pressed until it is released.
XAML
<!--
Delay: The time, in milliseconds, the RepeatButton waits
when it is pressed before it starts repeating the click action.
Interval: The time, in milliseconds, between repetitions
of the click action, as soon as repeating starts.
-->
<RepeatButton Content='Buy'
Interval='50' Delay='100'
Click='RepeatButton_Click' />
Code
private float buyCounter = 0;
private void RepeatButton_Click(object sender, RoutedEventArgs e) {
buyCounter += 1;
buyClickerButtonCost.Text = buyCounter.ToString();
}
It's like #Andrew said - DispatcherTimer works on UI thread and with so small intervall you are blocking it.
If you want such a small interval you can use Timer on different Thread:
public MainPage()
{
InitializeComponent();
System.Threading.Timer myTimer = new Timer(MyTimerCallback);
myTimer.Change(1000, 10);
}
private static int value = 0;
private static void MyTimerCallback(object state)
{
value++;
}
But you must remember that you use it on different Thread - this Timer has no access to your UI elements (buttons and so on).
EDIT
You convinced me to check it:
static float clicks = 0;
static float clickers = 0;
static float clickerCost = 5;
static long savedTime = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;
static bool buyClickerButtonFlag = false;
static bool clickButtonFlag = false;
public MainPage()
{
InitializeComponent();
first.Click += ShowCounter;
DispatcherTimer t = new DispatcherTimer();
t.Interval = TimeSpan.FromSeconds(5);
t.Tick += ShowCounter;
t.Start();
System.Threading.Timer myTimer = new Timer(MyTimerCallback);
myTimer.Change(10, 10);
}
private void ShowCounter(object sender, EventArgs e)
{
textBlck.Text = clicks.ToString();
}
private static void MyTimerCallback(object state)
{
clicks++; // added to check running
if (true)
{
long nowTime = savedTime;
long timePassed = nowTime - savedTime;
//user input
if (clickButtonFlag)
{
clickButtonFlag = false;
clicks++;
System.Diagnostics.Debug.WriteLine("clicked!" + clicks);
}
if (buyClickerButtonFlag)
{
buyClickerButtonFlag = false;
if (clicks > clickerCost)
{
clickers++;
clicks -= clickerCost;
clickerCost *= 1.6F;
}
System.Diagnostics.Debug.WriteLine("clicker bought!" + clickers);
}
//update vars
if (timePassed > TimeSpan.TicksPerSecond)
{
savedTime = nowTime;
nowTime = DateTime.Now.Ticks / TimeSpan.TicksPerSecond;
clicks += clickers;
}
}
}
I tested it on the device and buttons works.
On the other hand - what's the point of putting a method that waits for a flag buton click, when you can put the job easily to button click event. Let it happen when user clicked button - don't check buton state all the time.

Executing method every hour on the hour

I want to execute a method every hour on the hour. I wrote some code,but it is not enough for my aim. Below code is working every 60 minutes.
public void Start()
{
System.Threading.Timer timerTemaUserBilgileri = new System.Threading.Timer(new System.Threading.TimerCallback(RunTakip), null, tmrTemaUserBilgileri, 0);
}
public void RunTakip(object temauserID)
{
try
{
string objID = "6143566557387";
EssentialMethod(objID);
TimeSpan span = DateTime.Now.Subtract(lastRunTime);
if (span.Minutes > 60)
{
tmrTemaUserBilgileri = 1 * 1000;
timerTemaUserBilgileri.Change(tmrTemaUserBilgileri, 0);
}
else
{
tmrTemaUserBilgileri = (60 - span.Minutes) * 60 * 1000;
timerTemaUserBilgileri.Change(tmrTemaUserBilgileri, 0);
}
watch.Stop();
var elapsedMs = watch.ElapsedMilliseconds;
}
catch (Exception ex)
{
timerTemaUserBilgileri.Change(30 * 60 * 1000, 0);
Utils.LogYaz(ex.Message.ToString());
}
}
public void EssentialMethod(objec obj)
{
//some code
lastRunTime = DateTime.Now;
//send lastruntime to sql
}
If you want your code to be executed every 60 minutes:
aTimer = new System.Timers.Timer(60 * 60 * 1000); //one hour in milliseconds
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
aTimer.Start();
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
//Do the stuff you want to be done every hour;
}
if you want your code to be executed every hour (i.e. 1:00, 2:00, 3:00) you can create a timer with some small interval (let's say a second, depends on precision you need) and inside that timer event check if an hour has passed
aTimer = new System.Timers.Timer(1000); //One second, (use less to add precision, use more to consume less processor time
int lastHour = DateTime.Now.Hour;
aTimer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
aTimer.Start();
private static void OnTimedEvent(object source, ElapsedEventArgs e)
{
if(lastHour < DateTime.Now.Hour || (lastHour == 23 && DateTime.Now.Hour == 0))
{
lastHour = DateTime.Now.Hour;
YourImportantMethod(); // Call The method with your important staff..
}
}
I agree with SeƱor Salt that the chron job should be the first choice. However, the OP asked for every hour on the hour from c#. To do that, I set up the first timed event to fire on the hour:
int MilliSecondsLeftTilTheHour()
{
int interval;
int minutesRemaining = 59 - DateTime.Now.Minute;
int secondsRemaining = 59 - DateTime.Now.Second;
interval = ((minutesRemaining * 60) + secondsRemaining) * 1000;
// If we happen to be exactly on the hour...
if (interval == 0)
{
interval = 60 * 60 * 1000;
}
return interval;
}
Timer timer = new Timer();
timer.Tick += timer_Tick;
timer.Enabled = true;
timer.Interval = MilliSecondsLeftTilTheHour();
The problem now is that if the above timer.Interval happens to be 45 minutes and 32 seconds, then the timer will continue firing every 45:32 not just the first time. So, inside the timer_Tick method, you have to readjust the timer.Interval to one hour.
void timer_Tick(object sender, EventArgs e)
{
// The Interval could be hard wired here to 60 * 60 * 1000 but on clock
// resets and if the job ever goes longer than an hour, why not
// recalculate once an hour to get back on track.
timer.Interval = MilliSecondsLeftTilTheHour();
DoYourThing();
}
Just a small comment based on /Anarion's solution that I couldn't fit into a comment.
you can create a timer with some small interval (let's say a second, depends on precision you need)
You don't need it to go with any precision at all, you're thinking "how do I check this hour is the hour I want to fire". You could alternatively think "How do I check the next hour is the hour I want to fire" - once you think like that you realise you don't need any precision at all, just tick once an hour, and set a thread for the next hour. If you tick once an hour you know you'll be at some point before the next hour.
Dim dueTime As New DateTime(Date.Today.Year, Date.Today.Month, Date.Today.Day, DateTime.Now.Hour + 1, 0, 0)
Dim timeRemaining As TimeSpan = dueTime.Subtract(DateTime.Now)
t = New System.Threading.Timer(New System.Threading.TimerCallback(AddressOf Method), Nothing, CType(timeRemaining.TotalMilliseconds, Integer), System.Threading.Timeout.Infinite)
How about something simpler? Use a one-minute timer to check the hour:
public partial class Form1 : Form
{
int hour;
public Form1()
{
InitializeComponent();
if(RunOnStartUp)
hour = -1;
else
hour = DateTime.Now.Hour;
}
private void timer1_Tick(object sender, EventArgs e)
{
// once per minute:
if(DateTime.Now.Hour != hour)
{
hour = DateTime.Now.Hour;
DailyTask();
}
}
private DailyTask()
{
// do something
}
}
Use a Cron Job on the server to call a function at the specified interval
Heres a link
http://www.thesitewizard.com/general/set-cron-job.shtml
What about trying the below code, the loop is determined to save your resources, and it is running every EXACT hour, i.e. with both minutes and seconds (and almost milliseconds equal to zero:
using System;
using System.Threading.Tasks;
namespace COREserver{
public static partial class COREtasks{ // partial to be able to split the same class in multiple files
public static async void RunHourlyTasks(params Action[] tasks)
{
DateTime runHour = DateTime.Now.AddHours(1.0);
TimeSpan ts = new TimeSpan(runHour.Hour, 0, 0);
runHour = runHour.Date + ts;
Console.WriteLine("next run will be at: {0} and current hour is: {1}", runHour, DateTime.Now);
while (true)
{
TimeSpan duration = runHour.Subtract(DateTime.Now);
if(duration.TotalMilliseconds <= 0.0)
{
Parallel.Invoke(tasks);
Console.WriteLine("It is the run time as shown before to be: {0} confirmed with system time, that is: {1}", runHour, DateTime.Now);
runHour = DateTime.Now.AddHours(1.0);
Console.WriteLine("next run will be at: {0} and current hour is: {1}", runHour, DateTime.Now);
continue;
}
int delay = (int)(duration.TotalMilliseconds / 2);
await Task.Delay(30000); // 30 seconds
}
}
}
}
Why is everyone trying to handle this problem with a timer?
you're doing two things... waiting until the top of the hour and then running your timer every hour on the hour.
I have a windows service where I needed this same solution. I did my code in a very verbose way so that it is easy to follow for anyone. I know there are many shortcuts that can be implemented, but I leave that up to you.
private readonly Timer _timer;
/// starts timer
internal void Start()
{
int waitTime = calculateSleepTime();
System.Threading.Thread.Sleep(waitTime);
object t = new object();
EventArgs e = new EventArgs();
CheckEvents(t, e);
_timer.Start();
}
/// runs business logic everytime timer goes off
internal void CheckEvents(object sender, EventArgs e)
{
// do your logic here
}
/// Calculates how long to wait until the top of the hour
private int calculateSleepTime()
{
DateTime now = DateTime.Now;
int minutes = now.Minute * 60 * 1000;
int seconds = now.Second * 1000;
int substrahend = now.Millisecond + seconds + minutes;
int minuend = 60 * 60 * 1000;
return minuend - substrahend;
}
Here's a simple, stable (self-synchronizing) solution:
while(true) {
DoStuff();
var now = DateTime.UtcNow;
var previousTrigger = new DateTime(now.Year, now.Month, now.Day, now.Hour, 0, 0, now.Kind);
var nextTrigger = previousTrigger + TimeSpan.FromHours(1);
Thread.Sleep(nextTrigger - now);
}
Note that iterations may be skipped if DoStuff() takes longer than an hour to execute.

C# timer stop after some number of ticks automatically

How to stop a timer after some numbers of ticks or after, let's say, 3-4 seconds?
So I start a timer and I want after 10 ticks or after 2-3 seconds to stop automatically.
Thanks!
You can keep a counter like
int counter = 0;
then in every tick you increment it. After your limit you can stop timer then. Do this in your tick event
counter++;
if(counter ==10) //or whatever your limit is
yourtimer.Stop();
When the timer's specified interval is reached (after 3 seconds), timer1_Tick() event handler will be called and you could stop the timer within the event handler.
Timer timer1 = new Timer();
timer1.Interval = 3000;
timer1.Enabled = true;
timer1.Tick += new System.EventHandler(timer1_Tick);
void timer1_Tick(object sender, EventArgs e)
{
timer1.Stop(); // or timer1.Enabled = false;
}
i generally talking because you didn't mention which timer, but they all have ticks... so:
you'll need a counter in the class like
int count;
which you'll initialize in the start of your timer, and you'll need a dateTime like
DateTime start;
which you'll initialize in the start of your timer:
start = DateTime.Now;
and in your tick method you'll do:
if(count++ == 10 || (DateTime.Now - start).TotalSeconds > 2)
timer.stop()
here is a full example
public partial class meClass : Form
{
private System.Windows.Forms.Timer t;
private int count;
private DateTime start;
public meClass()
{
t = new Timer();
t.Interval = 50;
t.Tick += new EventHandler(t_Tick);
count = 0;
start = DateTime.Now;
t.Start();
}
void t_Tick(object sender, EventArgs e)
{
if (count++ >= 10 || (DateTime.Now - start).TotalSeconds > 10)
{
t.Stop();
}
// do your stuff
}
}
Assuming you are using the System.Windows.Forms.Tick. You can keep track of a counter, and the time it lives like so. Its a nice way to use the Tag property of a timer.
This makes it reusable for other timers and keeps your code generic, instead of using a globally defined int counter for each timer.
this code is quiet generic as you can assign this event handler to manage the time it lives, and another event handler to handle the specific actions the timer was created for.
System.Windows.Forms.Timer ExampleTimer = new System.Windows.Forms.Timer();
ExampleTimer.Tag = new CustomTimerStruct
{
Counter = 0,
StartDateTime = DateTime.Now,
MaximumSecondsToLive = 10,
MaximumTicksToLive = 4
};
//Note the order of assigning the handlers. As this is the order they are executed.
ExampleTimer.Tick += Generic_Tick;
ExampleTimer.Tick += Work_Tick;
ExampleTimer.Interval = 1;
ExampleTimer.Start();
public struct CustomTimerStruct
{
public uint Counter;
public DateTime StartDateTime;
public uint MaximumSecondsToLive;
public uint MaximumTicksToLive;
}
void Generic_Tick(object sender, EventArgs e)
{
System.Windows.Forms.Timer thisTimer = sender as System.Windows.Forms.Timer;
CustomTimerStruct TimerInfo = (CustomTimerStruct)thisTimer.Tag;
TimerInfo.Counter++;
//Stop the timer based on its number of ticks
if (TimerInfo.Counter > TimerInfo.MaximumTicksToLive) thisTimer.Stop();
//Stops the timer based on the time its alive
if (DateTime.Now.Subtract(TimerInfo.StartDateTime).TotalSeconds > TimerInfo.MaximumSecondsToLive) thisTimer.Stop();
}
void Work_Tick(object sender, EventArgs e)
{
//Do work specifically for this timer
}
When initializing your timer set a tag value to 0 (zero).
tmrAutoStop.Tag = 0;
Then, with every tick add one...
tmrAutoStop.Tag = int.Parse(tmrAutoStop.Tag.ToString()) + 1;
and check if it reached your desired number:
if (int.Parse(tmrAutoStop.Tag.ToString()) >= 10)
{
//do timer cleanup
}
Use this same technique to alternate the timer associated event:
if (int.Parse(tmrAutoStop.Tag.ToString()) % 2 == 0)
{
//do something...
}
else
{
//do something else...
}
To check elapsed time (in seconds):
int m = int.Parse(tmrAutoStop.Tag.ToString()) * (1000 / tmrAutoStop.Interval);

Categories

Resources