Include hour in timer countdown - c#

I am trying to create a countdown that includes hours.
private int time = 3660;
public MainWindow()
{
var vm = new TimerViewModel();
InitializeComponent();
// get display setting - 2 means extended
int displayType = Screen.AllScreens.Length;
// set the windows datacontext
DataContext = vm;
// set up the timedispatcher
dt.Interval = new TimeSpan(0, 0, 1);
dt.Tick += Timer_Tick;
}
private void Timer_Tick(object sender, EventArgs e)
{
switch(time)
{
case int x when x > 10 && x <= 20:
TimerPreview.Foreground = Brushes.Orange;
time--;
break;
.....................
default:
TimerPreview.Foreground = Brushes.LimeGreen;
time--;
break;
}
TimerPreview.Content = string.Format("00:{0:00}:{1:00}", time / 60, time % 60);
}
I cannot work out how to get the countdown working correctly with hours. It works great with minutes and seconds.
TimerPreview.Content = string.Format("{0:00}:{1:00}:{2:00}", time ???, time ??? 60, time % 60);
I have tried a number of combinations but have failed to find a solution. What am I missing? Many thanks.

Use 3600 (the number of seconds in an hour), and use the modulus operator on the minutes just like you're doing on the seconds (since you want 60 minutes to appear to roll over into a new hour):
TimerPreview.Content =
string.Format("{0:00}:{1:00}:{2:00}", time / 3600, (time / 60) % 60, time % 60);
// 320 -> 00:05:20
// 7199 -> 01:59:59
// 7201 -> 02:00:01

Another (arguably more readable) option would be to use a TimeSpan to handle the formatting:
TimerPreview.Content = TimeSpan.FromSeconds(time).ToString(#"hh\:mm\:ss");
Result when time is 3660:
01:01:00
EDIT: Thanks to #GrantWinney for pointing out that the default string format for a TimeSpan is the same as above, unless the timespan is greater than a day, in which case it includes days as well. So you could just do:
TimerPreview.Content = TimeSpan.FromSeconds(time).ToString();

Related

C# - Inconsistent timing with Progress Bars and Timers

I'm trying to make a smooth progress bar that "loads" from 0 to 100 based on a time value (in seconds).
So far, I've got a method that sets up the progress bar's maximum value to 100 * loadTime. Additionally, I've got a timer in my form, and its increment is 10 milliseconds. The idea is that the progress bar's maximum, divided by the timer's increment, will equal the number of seconds the progress bar needs to load.
Unfortunately, there are some inconsistencies with my timer. For instance, if I set my timer's increment to 1000 milliseconds and the bar's maximum to loadTime, it will be somewhat consistent, but it doesn't do anything for the first second. It's also very jittery. At 100 milliseconds and 10 * loadTime, it's slightly more consistent, but still very jittery. 10 milliseconds seems to be the sweet spot in terms of smoothness, however, if for instance, loadTime is equal to 5, it will load the progress bar in approximately 7 or 8 seconds.
I have also tried setting the timer's increment to 1, and my bar's maximum to 1000 * loadTime, however, this just makes it slower, and results in times of 10-13 seconds, when it should be 5 for instance.
Why's this the case? Can anything be done about it?
DisplayTimeProgressBar.cs (credit to Crispy's answer)
[DesignerCategory("code")]
public class DisplayTimeProgressBar : ProgressBar
{
public DisplayTimeProgressBar()
{
this.SetStyle(ControlStyles.UserPaint, true);
}
protected override void OnPaint(PaintEventArgs e)
{
Rectangle rec = e.ClipRectangle;
rec.Width = (int)(rec.Width * ((double)Value / Maximum));
rec.Height = rec.Height;
e.Graphics.FillRectangle(Brushes.Aquamarine, 0, 0, rec.Width, rec.Height);
}
}
Form.Designer.cs
private System.Windows.Forms.Timer progressBarTimer = new System.Windows.Forms.Timer(this.components);
private DisplayTimeProgressBar displayTimeProgressBar = new DisplayTimeProgressBar();
Form.cs
private void loadBar(int timeToDisplay)
{
this.displayTimeProgressBar.Visible = true;
this.displayTimeProgressBar.Maximum = timeToDisplay * 100;
this.progressBarTimer.Start();
}
private void progressBarTimer_Tick(object sender, EventArgs e)
{
if (this.displayTimeProgressBar.Value >= this.displayTimeProgressBar.Maximum)
{
this.displayTimeProgressBar.Value = 0;
this.displayTimeProgressBar.Visible = false;
this.progressBarTimer.Stop();
}
else
{
this.displayTimeProgressBar.Value++;
}
}
The following is a visual demonstration of my issue. The parameter loadTime has been set to 5 (5 seconds). In this demo, the timer's increment is 10, and the bar's maximum is 500.
If you want a continous animation over a specifc duration, wouldn't you have to base it on frames per second (i.e. 40 fps), as well as taking into consideration the maximum width in pixels of the monitor, because the Value corresponds to the number of pixels filled. In other words, using 100 for the Maximum value doesn't make sense. So then sleep 25 millis, calculate the true elapsed time (current time minus start time), divide the elapsed time by the total duration. Now you have a percentage. Multiply that percentage by the Maximum value, and that is the current Value. Also, setting pb.Style = ProgressBarStyle.Continuous; seems to look better, but it may not apply to your situation since you are using a custom paint. Example:
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Form f5 = new Form();
ProgressBar pb = new ProgressBar { Dock = DockStyle.Top, Minimum = 0, Maximum = 2000 };
pb.Style = ProgressBarStyle.Continuous;
double durationSeconds = 5;
Button btnStart = new Button { Text = "Start", Dock = DockStyle.Bottom };
double fps = 40; // refreshes per second
btnStart.Click += delegate {
int maxValue = pb.Maximum;
Thread t = new Thread(() => {
DateTime utcNow = DateTime.UtcNow;
int sleepMillis = (int) (1000 / fps);
int progress = 0;
while (true) {
Thread.Sleep(sleepMillis);
DateTime utcNow2 = DateTime.UtcNow;
double elapsedSeconds = (utcNow2 - utcNow).TotalSeconds;
int newProgress = (int) Math.Round(maxValue * elapsedSeconds / durationSeconds);
if (newProgress >= maxValue) {
pb.BeginInvoke((Action) delegate {
pb.Value = maxValue;
});
break;
}
else if (newProgress > progress) {
pb.BeginInvoke((Action) delegate {
pb.Value = newProgress;
});
}
progress = newProgress;
}
});
t.IsBackground = true;
t.Start();
};
f5.Controls.Add(pb);
f5.Controls.Add(btnStart);
Application.Run(f5);

C# Countdown timer, no accuracy. Looking for different way to do this

I have a count down timer, when running it seems to get behind by 1 second every 5 - 10 seconds.
Is there any way to make this countdown timer be accurate to system clock time?
Public MAIN()
timer = new DispatcherTimer();
timer.Interval = TimeSpan.FromMilliseconds(1000);
timer.Tick += timer_Tick;
void timer_Tick(object sender, object e)
{
basetime = basetime - 1;
txt.Text = string.Format("{0:00}:{1:00}:{2:00}", basetime / 3600, (basetime / 60) % 60, basetime % 60);
if (basetime == 0)
{
timer.Stop();
Timer_Start.IsEnabled = Timer_Pause.IsEnabled = Timer_Restart.IsEnabled = true;
}
}
i would love to try a few different ideas, also i will have to pause and start the timer periodically so will have to work that in to the solution as well.
Instead of trying to compute the time from an iterator, compute it using the system clock.
Change basetime's type to DateTime.
When you start the timer, set the base time: basetime = DateTime.Now;
And set a TimeSpan variable to the amount of time to countdown: countdown = new TimeSpan(/*your code here*/);
Now update your timer_Tick like so:
void timer_Tick (object sender, object e)
{
var now = DateTime.Now;
// This computes the remaining time and formats it for display
txt.Text = (now - basetime).ToString("hh:mm:ss");
// Checks that the timer is complete
if (now - basetime > countdown)
{
/* stop the timer */
}
}
DispatcherTimer isn't perfectly accurate, it is approximate, which for most tasks is good enough, but for keeping track of time is inadequate. Instead you may use it to approximate a second, and then update your timer with the actual time from the system clock.
Solution;
Run a stop watch and compare the math to it.
P.S. I know there is better ways of doing this and may update in future releases.
void Timer_Tick(object sender, object e)
{
int BaseINMill = basetime * 1000;
//total of milliseconds left
basetimeMILL.Text = BaseINMill.ToString() ;
//Display the total Milliseconds elapsed
ConsolePost.Text = VerifyMill.ElapsedMilliseconds.ToString();
if( CorrectionWatch.ElapsedMilliseconds >= 2000)
{
PreCorrBaseTime = PreCorrBaseTime - 2;
//Display the Correction timer Milliseconds elapsed
Correctiontest.Text = CorrectionWatch.ElapsedMilliseconds.ToString();
CorrectionWatch.Restart();
//Show the total time between seconds changing on screen
ConsoleOutputPre.Text = stopwatch.ElapsedMilliseconds.ToString();
basetime = PreCorrBaseTime;
txt.Text = string.Format("{0:00}:{1:00}:{2:00}", basetime / 3600, (basetime / 60) % 60, basetime % 60);
if (basetime == 0)
{
timer.Stop();
Timer_Start.IsEnabled = Timer_Pause.IsEnabled = Timer_Restart.IsEnabled = true;
VerifyMill.Stop();
}
stopwatch = Stopwatch.StartNew();
}
else {
if (stopwatch.ElapsedMilliseconds >= 975 && stopwatch.ElapsedMilliseconds <=1025 )
{
//Show the total time between seconds changing on screen
ConsoleOutputPre.Text = stopwatch.ElapsedMilliseconds.ToString();
basetime = basetime - 1;
txt.Text = string.Format("{0:00}:{1:00}:{2:00}", basetime / 3600, (basetime / 60) % 60, basetime % 60);
if (basetime == 0)
{
timer.Stop();
Timer_Start.IsEnabled = Timer_Pause.IsEnabled = Timer_Restart.IsEnabled = true;
VerifyMill.Stop();
}
stopwatch = Stopwatch.StartNew();
}
if (stopwatch.ElapsedMilliseconds >= 1026 && stopwatch.ElapsedMilliseconds <= 2000)
{
//Show the total time between seconds changing on screen
ConsoleOutputPre.Text = stopwatch.ElapsedMilliseconds.ToString();
basetime = basetime - 1;
txt.Text = string.Format("{0:00}:{1:00}:{2:00}", basetime / 3600, (basetime / 60) % 60, basetime % 60);
if (basetime == 0)
{
timer.Stop();
Timer_Start.IsEnabled = Timer_Pause.IsEnabled = Timer_Restart.IsEnabled = true;
VerifyMill.Stop();
}
stopwatch = Stopwatch.StartNew();
}
if (stopwatch.ElapsedMilliseconds > 2000)
{
ErrorPrompt();
VerifyMill.Stop();
}
}
}

Timer Countdown Time Format

I have built a countdown timer in WPF following the tutorial here: Simple COUNTDOWN With C# - WPF
Here is the code snippet I'm using:
void Timer_Tick(object sender, EventArgs e)
{
if (time > 0)
{
if (time <= 60)
{
RebootCountdownWindowTimerText.Foreground = Brushes.Red;
time--;
RebootCountdownWindowTimerText.Text
= string.Format("00:0{0}:{1}", time / 60, time % 60);
}
time--;
RebootCountdownWindowTimerText.Text
= string.Format("00:0{0}:{1}", time / 60, time % 60);
}
else
{
Timer.Stop();
serviceController.RebootComputer();
}
}
Problem is, when the timer counts down into lower seconds, the format changes. For example, counting down from 1 minute and 12 seconds:
00:01:12
00:01:11
00:01:10
00:01:9
00:01:8
etc...
How can I re-factor the code so that it properly shows a 0 in the "tens" place when counting down?
Use the Number format D#, where # determines the number of digits so for example
var time =112;
Console.WriteLine(string.Format("00:{0:D2}:{1:D2}", time / 60, time % 60));
will give
00:01:52

Why the milliseconds are not working?

I want to display in label9 a timer counting: Hours Minutes Seconds Milliseconds
Timer1 interval set to 1000
I'm calling this method from the timer1 tick event:
private void NewsUpdate()
{
newText = new List<string>();
counter += 1;
TimeSpan t = TimeSpan.FromSeconds(counter);
string time = string.Format("{0:D2}h:{1:D2}m:{2:D2}s:{3:D3}ms",
t.Hours,
t.Minutes,
t.Seconds,
t.Milliseconds);
progressBar1.Value = counter;
label9.Text = time;
}
I see the seconds moving counting but the milliseconds stay still on 000 why the milliseconds are not working ?
Set the Interval of the timer to 1 and create TimeSpan object from milliseconds.
Update ur code as follows
timer1.Interval = 1;
private void NewsUpdate()
{
counter += 1;
TimeSpan t = TimeSpan.FromMilliseconds(counter);
string time = string.Format("{0:D2}h:{1:D2}m:{2:D2}s:{3:D4}ms",
t.Hours,
t.Minutes,
t.Seconds,
t.Milliseconds);
progressBar1.Value = Convert.ToInt32(counter / 1000);
label9.Text = time;
}
Because you set the timespan directly from the number of whole seconds. Any timespan from an integer number of seconds will always have 0 milliseconds.
What you probably want is to either set your timespan to Date.Now(), or record the current time at timer start and subtract that from the current time on each timer tick.

Timer in XNA framework

I have a problem with my game. It is divided in a two states: GameState.MainMenu and GameState.Playing.
I want to draw on screen the timer when I playing. I use gameTime.TotalGameTime.Minutesand
gameTime.TotalGameTime.Seconds variable. But when I clicked on button Play Now, and my game switched state from GameState.MainMenu to GameState.Playing., the timer dosent start from 0. It starts with the time, which elapsed when I spend in MainMenu. I try create next variable to count time, which I spent in MainMenu, and I try to subtract from the first variable, but the displaying time is not properly.
My substracting time:
minutesPlaying = gameTime.TotalGameTime.Minutes; ;
secondsPlaying = gameTime.TotalGameTime.Seconds;
switch (currentGameState)
{
case GameState.MainMenu:
minutesMenu = gameTime.TotalGameTime.Minutes;
secondsMenu = gameTime.TotalGameTime.Seconds;
if (btnPlay.isTapped == true)
{
currentGameState = GameState.Playing;
soundEffectInstance.Stop();
}
btnPlay.Update(collection);
break;
case GameState.Playing:
minutesTotal = minutesPlaying - minutesMenu;
secondsTotal = secondsPlaying - secondsMenu;
break;
}
Invoke my method:
timer.Update(minutesTotal, secondsTotal);
Update method:
public void Update(int min, int sec)
{
string seconds, minutes;
seconds = sec.ToString();
minutes = min.ToString();
if (sec <= 9) seconds = "0" + seconds;
if (min <= 9) minutes = "0" + minutes;
nowString = minutes + ":" + seconds;
}
Thanks for answer :)
I would use a 'StopWatch' to 'Start' and 'Pause' the tracking of time spent in your playing state. When the playing state is entered start the 'StopWatch' and then pause it when leaving that state.
Your on the right track, but I think you should have a time passed variable instead of having to subtract it from the original gameTime, this way if you go back to the menu, then back into the game, your method wont be broken. Basicly it just takes a TimeSpan at 0 and adds gameTime to it as time passes, giving you the amount of time spent in the level.
First off add a new TimeSpan
public TimeSpan TimePassed;
Now in your Update() method inside case GameState.Playing: you will need to incriment the timer.
TimePassed -= gameTime.ElapsedGameTime;
You can also reset the timer to Zero if you need to make a new level (If that applies, Ex: Replaying a level will need a timer reset)
Now to render the time in your Draw() method.
string timeString = TimePassed.Minutes.ToString("00") + ":" + TimePassed.Seconds.ToString("00");
spriteBatch.DrawString(FONT,timeString,POSITION,Color.White);
And there you go!
You should modify time only in Playing state, I have modified the code to achieve it, whithout too much changes... PlayTime keeps the total time elapsed in the playing state.
TimeSpan Elapsed ;
switch (currentGameState)
{
case GameState.MainMenu:
if (btnPlay.isTapped == true)
{
currentGameState = GameState.Playing;
soundEffectInstance.Stop();
}
btnPlay.Update(collection);
Elapsed = TimeSpan.Zero;
break;
case GameState.Playing:
Elapsed = gametime.ElapsedGameTime;
break;
}
Invoke my method:
timer.Update(Elapsed);
Update method:
TimeSpan PlayTime = TimeSpan.Zero;
public void Update(TimeSpan Elapsed)
{
PlayTime.Add(Elapsed);
string seconds, minutes;
seconds = PlayTime.Seconds.ToString();
minutes = PlayTime.Minutes.ToString();
if (sec <= 9) seconds = "0" + seconds;
if (min <= 9) minutes = "0" + minutes;
nowString = minutes + ":" + seconds;
}
Try this:
Before the game starts you don't need to keep track of the time, so I have set minutesPlaying and secondsPlaying to 0 in GameState.MainMenu. Don't initialize minutesPlaying and secondsPlaying within the main Update() function.
minutesPlaying = 0;
secondsPlaying = 0;
switch (currentGameState)
{
case GameState.MainMenu:
if (btnPlay.isTapped == true)
{
currentGameState = GameState.Playing;
soundEffectInstance.Stop();
}
btnPlay.Update(collection);
break;
case GameState.Playing:
secondsPlaying += gameTime.ElapsedGameTime.Seconds;
if (secondsPlaying >= 60)
{
minutesPlaying++;
secondsPlaying = 0;
}
break;
}
When you display the time, just display minutesPlaying and secondsPlaying. You don't need any extra variables.
If that doesn't work, you can always keep track of the milliseconds (secondsPlaying += gameTime.ElapsedGameTime.Milliseconds) that pass when the game starts and divide the value by 1000 to display the seconds.

Categories