Dispose isn't working in my code for some reason. When i hit the reset button, and then start again, it won't start the timer over. I've tried disabling and reenablinig enabled(), but it still didn't work.
Here's my code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp1
{
public partial class StopWatch : Form
{
bool reSet = false;
bool stopped = true;
public StopWatch()
{
InitializeComponent();
}
private void startStop_Click(object sender, EventArgs e)
{
timer1.Enabled = true;
if (stopped == true)
{
timer1.Start();
stopped = false;
}
else
{
timer1.Stop();
stopped = true;
}
}
private void reset_Click(object sender, EventArgs e)
{
timer1.Dispose();
txtBox.Text = "";
reSet = true;
stopped = true;
}
int i = 0;
private void timer1_Tick(object sender, EventArgs e)
{
txtBox.Text = i.ToString();
i++;
}
}
}
Forget the timer for now, just make a stopwatch:
DateTime startTime = DateTime.MinValue; //it's reset
StartButton_Click(...){
startTime = DateTime.Now;
}
StopButton_Click(...){
MessageBox.Show("counted secs: " + (DateTime.Now - startTime).TotalSeconds);
}
That's a stopwatch
Now let's make it look like it runs. Add a label to the Form. Add a timer to the form, set it's interval to 100, enable it and put a tick event:
Timer_Tick(...){
StopWatchLabel.Text = startTime == DateTime.MinValue ? "00:00:00.000" : (DateTime.Now - startTime).ToString();
}
Now you can get into the minutiae of adding a reset button (set startTime to MinValue) starting and stopping the timer (no point updating a label to 00:00 ten times a second, but no harm in it either) but hopefully this proves that the timer is not (and should not) be part of the stopwatch function of measuring the passage of time. The timer doesn't need messing with/disposing etc. It's purely to update a label with the period that has passed since your start time
Related
I am wondering how to constantly update an readonly textbox.
The text box displays a text that always changes.
My problem is if I create an loop the application won't start and if I start the loop using a button my application freezes and only it only runs the loop.
I also can't use a new thread or the thread that I use to change the variables that are displayed within the text because in this case I just get an error System.InvalidOperationException
I was searching for anwser but I couldn't find one.
When using a thread you have to cause your ui update work to run on the UI thread, and that's where you use an "invoke".
There are many ways to achieve your goal, I'll show you two ways you can do it:
using a thread (BackgroundWorker is just a fancier way to do that)
a Timer (it might be overkill to use a thread just to update a
counter if that is what you are intending).
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApp2
{
public partial class Form1 : Form
{
bool m_shutdown = false;
int m_counter = 0;
Timer m_timer = new Timer();
BackgroundWorker m_backgroundworker = new BackgroundWorker();
bool m_usetimerway = false; // change this to true to try the timer way
Action m_actionupdatecounter;
public Form1()
{
InitializeComponent();
m_actionupdatecounter = new Action(() =>
{
UpdateCounter();
});
}
private void Form1_Load(object sender, EventArgs e)
{
if (m_usetimerway)
{
m_timer.Interval = 50;
m_timer.Tick += M_timer_Tick;
m_timer.Enabled = true;
}
else
{
m_backgroundworker.DoWork += M_backgroundworker_DoWork;
m_backgroundworker.RunWorkerCompleted += M_backgroundworker_RunWorkerCompleted;
m_backgroundworker.RunWorkerAsync();
}
}
void UpdateCounter()
{
if (this.InvokeRequired)
{
// Get it to be run on the UI thread
this.BeginInvoke( m_actionupdatecounter );
}
else
{
m_counter++;
textBoxCounter.Text = string.Format("{0}", m_counter);
}
}
private void M_timer_Tick(object sender, EventArgs e)
{
// This is already on the UI thread because it's a "WinForms" timer
UpdateCounter();
}
private void M_backgroundworker_DoWork(object sender, DoWorkEventArgs e)
{
while (!m_shutdown)
{
UpdateCounter();
System.Threading.Thread.Sleep(50);
}
}
private void M_backgroundworker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
m_shutdown = true;
// To have a more graceful shutdown, you might want to wait for the
// background worker to have "completed" before you actually exit
// your winforms app.
}
}
}
I'm trying to make a function run every 200 milliseconds so that it can show the time difference between when the program first started and right now. I tried threading with this code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Timers;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace ComputerTimer
{
public partial class MainWindow : Window
{
private DateTime startTime, endTime;
private static System.Timers.Timer timer1;
private bool running = true;
public MainWindow()
{
InitializeComponent();
startTime = DateTime.Now;
//makes new timer with 200 milliseconds interval
timer1 = new System.Timers.Timer(200);
timer1.Elapsed += new ElapsedEventHandler(timer1_Tick);
timer1.Interval = 200;
timer1.Enabled = true;
}
private void timer1_Tick(object sender, EventArgs e)
{
while (running)
{
endTime = DateTime.Now;
TimeSpan span = endTime - startTime; //gets difference between now and when the program was started
Title.Content = span.ToString().Substring(0, 8); //gets first 8 characters (taking out milliseconds)
}
}
private void btnStop_Click(object sender, RoutedEventArgs e)
{
//when button is pressed to stop timer
running = false;
}
}
}
But this just throws the exception 'InvalidOperationException' and says "Additional information: The calling thread cannot access this object because a different thread owns it." about line 48
Title.Content = span.ToString().Substring(0, 8); //gets first 8 characters (taking out milliseconds)
I'm quite confused what to do from here and have searched all over stack overflow looking for an answer but nothing seems to work. I have also tried DispatcherTimer but with no luck.
Edit: This is the answer which worked for me for anyone looking over this in the future
namespace ComputerTimer
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
private DateTime startTime, endTime;
private DispatcherTimer dtClockTime;
public MainWindow()
{
InitializeComponent();
startTime = DateTime.Now;
dtClockTime = new DispatcherTimer();
dtClockTime.Interval = new TimeSpan(0, 0, 0, 0, 200); //in days, Hour, Minutes, Seconds, millis
dtClockTime.Tick += dtClockTime_Tick;
dtClockTime.Start();
}
private void dtClockTime_Tick(object sender, EventArgs e)
{
endTime = DateTime.Now;
TimeSpan span = endTime - startTime; //gets difference between now and when the program was started
Title.Content = span.ToString().Substring(0, 8);
}
private void btnStop_Click(object sender, RoutedEventArgs e)
{
//when button is pressed to stop timer
dtClockTime.Stop();
}
}
}
You've created an endless loop in the timer Tick event, you shouldnt have a while(x) loop in there.
private void timer1_Tick(object sender, EventArgs e)
{
endTime = DateTime.Now;
TimeSpan span = endTime - startTime; //gets difference between now and when the program was started
Title.Content = span.ToString().Substring(0, 8); //gets first 8 characters (taking out milliseconds)
}
And your stop button should just disable the timer
private void btnStop_Click(object sender, RoutedEventArgs e)
{
timer1.Enabled = false;
}
Edit: It might be that you need to set the SynchronisingObject of the timer
timer1.SynchronisingObject = this;
Failing the above it looks like for a wpf application (sorry, I initially missed the wpf tag) you should be using a DispatcherTimer in place of System.Timers.Timer.
The setup is much the same as your existing code, it just uses a different type of timer which raises the tick event on the correct (UI) thread.
As an aside, there is no need to string mash a DateTime object, there are methods for being able to format a timespan
Title.Content = span.ToString("mm:ss.ffff");
Only add SynchronisingObject parameter for your timer.
Like this:
timer1.SynchronisingObject = this;
I'm new to C# and I'm trying to make a custom chronometer that is composed of two labels (label1 and label2) that display time strings (time and time0/time1) and one button (pause/play) that changes its text from pause to play and viceversa on each click. Label1 shows time that is a string var maked by datetime.now (hhmmss), label2 shows time0 and after clicking on the button "pause" and again on "play" it will shows time1 (time 1 is calculated by the formula below).
It does the following:
get system datetime.now (hhmmss), saves it in time string and shows it in label1
pushing the button pause, saves the value of time in another string time0 and shows it stopped in label2
pushing the button play, starts the time of label2 (time1) that's not synchronized with the time of label1
To calculate time1 I would like to use this formula:
time1 = DateTime.Now - ((difference between DateTime.Now and time0) - 1 second)
I'm stucked on the 3rd point because I don't know how to do the time difference between two strings and use the new time time1 as text for label2 and next clicks.
This is my actual code, any help to complete it is appreciate, thank you.
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
//time0
public int hh = 0;
public int mm = 0;
public int ss = 0;
//time
public string time = "";
public string time0 = "";
public bool IsPause = true;
public Timer t = new Timer();
public Form1()
{
InitializeComponent();
}
private void label1_Click(object sender, EventArgs e)
{
}
private void Form1_Load(object sender, EventArgs e)
{
//timer interval
t.Interval = 1000; //in millisecondi
t.Tick += new EventHandler(this.t_Tick);
//start timer form loads
t.Start(); //questo userĂ il metodo t_Tick()
}
//timer eventhandler
private void t_Tick(object sender, EventArgs e)
{
//get current time
hh = DateTime.Now.Hour;
mm = DateTime.Now.Minute;
ss = DateTime.Now.Second;
//padding leading zero
if(hh < 10)
{
time += "0" + hh;
}
else
{
time += hh;
}
time += ":";
if(mm < 10)
{
time += "0" + mm;
}
else
{
time += mm;
}
time += ":";
if (ss < 10)
{
time += "0" + ss;
}
else
{
time += ss;
}
//update labels
label1.Text = time;
if (IsPause == false) label2.Text = time0;
else label2.Text = time;
}
private void button1_Click(object sender, EventArgs e)
{
if (button1.Text == "pause")
{
IsPause = false;
button1.Text = "play";
time0 = label1.Text;
}
else
{
IsPause = true;
button1.Text = "pause";
}
}
}
}
It sounds as if you are best saving the time in the controls as well as the time as a string. The Tag property is there for that purpose. See https://msdn.microsoft.com/en-us/library/system.windows.forms.control.tag%28v=vs.110%29.aspx
So, for example, if you set the DateTime you are using into label2.Tag to the same time as you format it as text in label2.Text then you can refer to it as a DateTime. Then when you need to calculate from it you can use
DateTime.Subtract - see https://msdn.microsoft.com/en-us/library/8ysw4sby%28v=vs.110%29.aspx
to determine the elapsed time.
So, to refer this to your code, wherever you have code like this, where time is the string you have created from a DateTime instance:
label1.Text = time;
you also need to set the time like this (DateTime.Now is an example, you should chose whatever you used to format the time string):
label1.Tag = DateTime.Now
Then later, when you want to know the time in label1, do this:
DateTime t = (DateTime)label1.Tag
I have a metronome project set up. I have a tap button which should check the tempo of your beat and average it out. Every bit of math works properly because I checked it with a calculator. Here is the code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Media;
namespace Metronome
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void timer3_Tick(object sender, EventArgs e)
{
// Convert tempo to timer1.Tick (miliseconds between each beat)
timer1.Interval = Convert.ToInt32(60000 / numericUpDown1.Value);
}
private void button1_Click(object sender, EventArgs e)
{
// Play / Pause button
if (button1.Text == "Go!") { timer1.Enabled = true; button1.Text = "Stop!"; }
else if (button1.Text == "Stop!") { timer1.Enabled = false; button1.Text = "Go!"; }
}
private void timer1_Tick(object sender, EventArgs e)
{
// The 'ding' sound for the metronome
SystemSounds.Beep.Play();
}
private void button2_Click(object sender, EventArgs e)
{
// Set the tempo to be the average of the convertion from miliseconds between 2 beats and the current tempo
if (timer2.Enabled) { numericUpDown1.Value = ((60000 / Tap) + numericUpDown1.Value) / 2; Tap = 0; }
else timer2.Enabled = true;
}
int Tap = 0;
private void timer2_Tick(object sender, EventArgs e)
{
// Get the amount of miliseconds between each beat
Tap++;
}
private void button3_Click(object sender, EventArgs e)
{
// Reset the tap timer
timer2.Enabled = false;
Tap = 0;
}
}
}
The problem is in timer2_Tick, because it should add 1 to Tap every milisecond, instead, when I tried it it goes to a tiny number like 20 or 30. How can I fix this?
There is a really good article I always rely on when selecting which timer to use:
http://msdn.microsoft.com/en-us/magazine/cc164015.aspx
I would suggest using one of the threaded options. Specifically, the article says of the the windows forms timer (System.Windows.Forms.Timer):
If you're looking for a metronome, you've come to the wrong place.
If you only need to check the amount of time passed between button taps, use a StopWatch. It gives you a high precision timing mechanism. There is no need for you to count milliseconds yourself.
I'm creating a countdown timer that allows the user to choose a valid time, and once the countdown reaches zero, it plays a sound.
I've instantiated a timer from the System.Timers namespace and set its Interval to 1 second. I convert the user-specified time to seconds, and decrement that value by 1 every time the Timer.Elapsed function hits. Once it reaches zero, that means the countdown has reached zero which means it's time to play the sound.
However, whenever it doesn't reach zero, I decrement the time value by 1 and I also want to increment the ProgressBar by using the progressbar.Increment function.
Unfortunately, whenever I do this, it gives me an exception having to do with multithreading issues. I know what is wrong, but I am not sure how to fix it. Do I need to start the timer.Elapsed function on a new thread?
The error is:
Cross-thread operation not valid: Control 'CountdownProgress' accessed
from a thread other than the thread it was created on.
Also, any tips on better programming habits are welcome as well.
Many thanks!
Here is the code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Timers;
using System.Media;
using System.Diagnostics;
using System.Threading;
namespace WindowsFormsApplication1
{
public partial class Form_CDA : Form
{
public Form_CDA()
{
InitializeComponent();
}
private bool m_CountdownActive; // declare global variables and instantiate timer.
private bool m_Cancelled;
public decimal seconds;
public decimal minutes;
public decimal hours;
public decimal time;
public System.Timers.Timer timer = new System.Timers.Timer();
private void Form_CDA_Load(object sender, EventArgs e)
{
m_Cancelled = false;
timer.AutoReset = false;
timer.Interval = 0;
m_CountdownActive = false;
richTextBox1.Text = "Hello, please select an hour between 0 and 100, a minute between 0 and 59, and a second between 0 and 59. The countdown timer will play a sound when finished.";
btn_Cancel.Enabled = false;
seconds = 0;
minutes = 0;
hours = 0;
time = 0;
m_StatusBar.Text = "Program properly loaded, waiting for user input"; // initialize variables.
}
private void btn_SetCountdown_Click(object sender, EventArgs e)
{
seconds = numUpDown_Seconds.Value;
minutes = numUpDown_Minutes.Value;
hours = numUpDown_Hours.Value;
time = (hours * 3600) + (minutes * 60) + seconds; // calculate the total time in seconds.
if (time != 0) // if time is not zero, start timer and set up event handler timer.elapsed.
{
timer.Interval = 1000;
timer.AutoReset = true;
timer.Start();
timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
CountdownProgress.Maximum = (int)time;
CountdownProgress.Minimum = 0;
CountdownProgress.Value = 0;
}
else
{
m_StatusBar.Text = "Invalid selection of times. Try again.";
return;
}
DateTime dt = DateTime.Now;
dt.AddSeconds((double)time);
Label_Countdown.Text = "Finishing time: " + dt.ToString(); // display end time to user.
m_CountdownActive = true;
btn_Cancel.Enabled = true;
btn_SetCountdown.Enabled = false;
numUpDown_Hours.Enabled = false;
numUpDown_Minutes.Enabled = false;
numUpDown_Seconds.Enabled = false; // disable controls.
m_Cancelled = true;
m_StatusBar.Text = "Timer set to " + numUpDown_Hours.Value.ToString() + " hours, " + numUpDown_Minutes.Value.ToString() + " minutes, " + numUpDown_Seconds.Value.ToString() + " seconds.";
}
void timer_Elapsed(object sender, ElapsedEventArgs e)
{
if (time == 0)
{
m_StatusBar.Text = "Countdown Finished";
SoundPlayer soundPlayer = new SoundPlayer(#"C:\Users\marupakuuu\Desktop\New stuff\doorbell.wav");
soundPlayer.Play(); // play sound.
timer.Stop();
return;
}
else
{
time = time - 1;
CountdownProgress.Increment(1); // exception occurs here; multithreading issues.
}
}
private void btn_Cancel_Click(object sender, EventArgs e)
{
// if user wishes to stop the countdown to start a new one.
m_Cancelled = true;
m_CountdownActive = false;
btn_SetCountdown.Enabled = true;
numUpDown_Seconds.Value = 0;
numUpDown_Minutes.Value = 0;
numUpDown_Hours.Value = 0;
numUpDown_Hours.Enabled = true;
numUpDown_Minutes.Enabled = true;
numUpDown_Seconds.Enabled = true;
btn_Cancel.Enabled = false;
m_StatusBar.Text = "Countdown cancelled";
}
}
}
A control can only be accessed within the thread it was created. Therefore use Invoke to execute the given code as a delegate on the main thread, where the control (progressbar) was created.
void timer_Elapsed(object sender, ElapsedEventArgs e)
{
if (time == 0)
{
m_StatusBar.Text = "Countdown Finished";
SoundPlayer soundPlayer = new SoundPlayer(#"C:\Users\marupakuuu\Desktop\New stuff\doorbell.wav");
soundPlayer.Play(); // play sound.
timer.Stop();
return;
}
else
{
time = time - 1;
Invoke(new Action(() => CountdownProgress.Increment(1)));
}
}
You may want to read http://msdn.microsoft.com/en-us/library/zyzhdc6b.aspx