I made a sample project. Where every 10 second it do some function. But when I try to show a timer tick in label it always stuck, and jump to certain second (eg: stuck at 9s and suddenly jump to 12s). What i want to ask,
Is my function run properly ?
Is my tick, skipped a few ms ? (it will overlap with my function)
How do I run it as a thread ?
My code
private void button2_Click(object sender, EventArgs e)
{
timer1.Start();
}
int x = 0;
private void timer1_Tick(object sender, EventArgs e)
{
x += 1;
label1.Text = x.ToString();
if (x % 10 == 0)
{
addpoint();
//MessageBox.Show("success");
}
}
how to keep my label1.text keep updating, while do addpoint() function
Note:
I have set timer1 interval = 1000
update
i test it with this.
public void addpoint()
{
string x = #"c:\test\a.txt";
string text = "haiaiaia";
using (FileStream fs = new FileStream(x, FileMode.Create))
{
Byte[] xx = Encoding.ASCII.GetBytes(text);
fs.Write(xx, 0, xx.Length);
}
Messagebox.show("Created !");
}
It looks like your using a Windows.Forms.Timer which is executed on the main thread. The advantage is that you don't need to call Invoke, the disadvantage is that addpoint is also executed on the main thread and hence blocks your GUI from update, when in the mean time the next tick events are fired.
You can verify it be replacing the call of addpoint with Thread.Sleep(3000) and you will experience the same behaviour.
What you could do is to try and run the method on another thread:
private void timer1_Tick(object sender, EventArgs e)
{
x += 1;
label1.Text = x.ToString();
if (x % 10 == 0)
{
Thread t = new Thread(addpoint);
t.Start();
}
}
This should avoid the blocking of the GUI.
Disclaimer:
It is important to know what you actually do in addpoint, because this solution might lead to race condition and wrong functioning of the method. For example if you are using class variables in it, and if the possibility exists that a second thread can be started while the first has not finished yet! Be aware.
Related
I have a little problem. There is something like chess timer. When i press button, current timer stops and second starts, but after 1 second. How can i start second one immediately?
using System;
using System.Windows.Forms;
namespace WindowsFormsApp1 {
public partial class Form1 : Form {
byte sec1;
byte sec2;
public Form1() {
InitializeComponent();
sec1 = 0;
sec2 = 0;
}
private void button1_Click(object sender , EventArgs e) {
timer1.Start();
timer2.Stop();
}
private void button2_Click(object sender , EventArgs e) {
timer2.Start();
timer1.Stop();
}
private void timer1_Tick(object sender , EventArgs e) {
label1.Text = sec1.ToString();
sec1++;
}
private void timer2_Tick(object sender , EventArgs e) {
label2.Text = sec2.ToString();
sec2++;
}
}
}
Edit
I know your question is "how to start the timers immediately", but in your code they are starting immediately. When you call start the timer starts. I believe the effect you are seeing is related to the delay associated with the tick event, which from the description I am assuming is set to a 1 second interval. Since you have said that you are trying to simulate something similar to a chess timer (although in your case counting up as opposed to down), then using something like a stop watch which can start, stop and show elapsed time would be a closer model. Since there is a Stopwatch class that provides exactly this behavior, I think it would be easier to implement it using two of those and just have a single background thread that updates the UI as frequently as needed. You could even add an update call into each button push to ensure the text boxes are up to date.
===============================
Maybe instead of the timers you should use two instances of the Stopwatch class. This will remove the need for your two variables that you are using to keep track of the seconds as the Stopwatch class will be holding the elapsed time for each counter.
Then in your button methods you could just do this:
private Stopwatch sw1 = new Stopwatch();
private Stopwatch sw2 = new Stopwatch();
private void button1_Click(object sender , EventArgs e) {
sw1.Start();
sw2.Stop();
}
private void button2_Click(object sender , EventArgs e) {
sw2.Start();
sw1.Stop();
}
And then you can use a Background worker or some other background thread that runs and updates your text boxes with the elapsed time from the timers you just need to grab the elapsed time.
// This will give you the total number of seconds elapsed.
var timer1Seconds = Math.Floor(sw1.Elapsed.TotalSeconds);
Here is an example of how you can make this update the UI:
private bool _stop = false;
public Form1()
{
InitializeComponent();
Task.Run(() =>
{
while(!_stop)
{
UpdateElapsedTimes();
Thread.Sleep(1000);
}
}
}
private void UpdateElapsedTimes()
{
if (InvokeRequired)
{
Invoke(UpdateElapsedTimes());
return;
}
label1.Text = Math.Floor(sw1.Elapsed.TotalSeconds).ToString();
label2.Text = Math.Floor(sw2.Elapsed.TotalSeconds).ToString();
}
Note - in a production program I would not use a boolean as my loop checker, you would use an event handle, and probably a couple of event handles if you wanted to allow pausing the updates, this is just to show an idea of how to do it. You could invoke directly from the thread method and drop the InvokeRequired check, but I added that for additional safety and since it was there I skipped it in the loop.
The timer does start immediately. The problem is that you are not reporting fractions of seconds, so the display will show 0 until a full second has elapsed, which is accurate, technically.
If you want to show 1 immediately, just initialize your variables that way.
public Form1() {
InitializeComponent();
sec1 = 1;
sec2 = 1;
}
This question already has answers here:
Can I use a timer to update a label every x milliseconds
(4 answers)
Closed 7 years ago.
I want to print "ms" value in textbox1 by clicking the button firstly, then it should wait for 100ms (one by one), then it show next value.
But it's showing only the last value by clicking button, still after coding Thread.Sleep(100). It should show the value like stop watch.
private void button1_Click(object sender, EventArgs e)
{
int ms = 00;
for (ms = 0; ms < 10; ms++)
{
textBox1.Text = ms.ToString();
Thread.Sleep(100);
textBox1.Text = "";
}
}
The reason your textbox only shows tha last value is because your code runs on the UI thread. The UI doesn't get the time to update its layout until your code has reached its end.
You could solve this by using some sort of background threading mechanism so that the UI has the time and resources to update itself.
You could use timers and update the textbox text on its tick event.
You could use task await to enforce background threading instead of Thread.Sleep
private async void button1_Click(Object sender, EventArgs e)
{
for (int ms = 0; ms < 10; ms++)
{
textBox1.Text = ms.ToString();
await Task.Delay(100);
}
}
Thats because you button click is running on the gui thread and will block painting.
You could try updating the textbox:
private void button1_Click(object sender, EventArgs e)
{
int ms = 00;
for (ms = 0; ms < 10; ms++)
{
textBox1.Text = ms.ToString();
textBox1.Update();
Thread.Sleep(100);
textBox1.Text = "";
}
}
But this will still block you gui thread. If you don't want to block the current thread, you could create a new Thread that invokes the gui thread to alter the textBox1.Text.
Writing to a TextBox from another thread?
I need to store the piano duration with Ticks as so then make the music note show according to that duration (Music players would know).
I'm using an interval of 100, but for some testing I used it at 1000.
The problem is this. When I'm invoking the method (I'm taking the 1000 millisecond interval one) the timer starts.. if I DO NOT manage to get the 1000 milliseconds it shows Duration 0: but then if I do for example 2 seconds, it shows 3 seconds, if I try to press it for another second (a different key) it would show 4 seconds instead of 1.
It's like it keeps on recurring. Same happened with the 100 interval one. It went mad. sometimes 40 sometimes 23 and so on. Any idea how to fix (resetting the timer)
N.B I'm using System.Windows.Forms.Timer as library
part of a method which invokes the methods further below
for (int i = 0; i < 15; i++)
{
WhiteKey wk = new WhiteKey(wKeys[i], wPos[i]-35,0); //create a new white Key with [i] Pitch, at that x position and at y =0 position
wk.MouseDown += onRightClick; //holds the Duration on Right Click
wk.MouseUp += onMouseUp;
wk.Click += new EventHandler(KeyClick); //Go to KeyClick Method whenever a key is pressed
this.panel1.Controls.Add(wk); //Give it control (to play and edit)
}
Methods controlling the time
private void onRightClick(object sender, MouseEventArgs e)
{
wk = sender as WhiteKey;
duration = 0;
t1.Enabled = true;
t1.Tick += timeTick;
t1.Interval = 100;
}
private void timeTick(object sender, EventArgs e)
{
duration++;
}
private void onMouseUp (object sender, MouseEventArgs e)
{
t1.Enabled = false;
String time = "Key: " + pitch + "\nDuration: " +duration ; //Test purposes to see if timer works
MessageBox.Show(time);
}
You are trying to measure time, don't use Timer, use Stopwatch.
You can find C# Stopwatch Exmples at dotnetpearls.com.
In abstract this is what you would want to do is something like this:
private void onRightClick(object sender, MouseEventArgs e)
{
stopwatch.Reset();
stopwatch.Start();
}
private void onMouseUp (object sender, MouseEventArgs e)
{
stopwatch.Stop();
String msg = "Duration in seconds: " + (stopwatch.ElapsedMilliseconds / 1000.0).ToString("0.00");
MessageBox.Show(msg);
}
Note: you may want to change the units or the string format.
Notes on using timer:
1) System.Windows.Forms.Timer uses the message loop of your window, this means that it may get delayed because the window is busy handling other events (such as click). For a better behaviour use System.Threading.Timer.
2) If using System.Windows.Forms.Timer don't set the Tick event handler each click. The event handler will execute once for each time you add it.
That is:
private void onRightClick(object sender, MouseEventArgs e)
{
wk = sender as WhiteKey;
duration = 0;
t1.Enabled = true;
//t1.Tick += timeTick; you should add this only once not each click
t1.Interval = 100;
}
3) If you use System.Threading.Timer you may want to make the variable duration volatile.
t1.Tick += timeTick;
By the way in your code sample you subscribe to the 'Tick' timer event each time on Right mouse click.
So if you click 2 times the
private void timeTick(object sender, EventArgs e)
method will be called twice, and 'duration++' will be executed twice. Your event subscription code should be executed only once for the timer.
P.S. If you need to measure duration, Timer is not the best way to do it.
I've looked at some guides and none of them have gotten me all the way there. I've never made a thread, discussed a thread, or seen a thread at the grocery store, so this may be a problem. Currently. I'm trying:
private void btnHUp_MouseDown(object sender, MouseEventArgs e)
{
{
ThreadStart HUp = new ThreadStart(dothis);
t = new Thread(HUp);
t.Start();
}
}
public void dothis()
{
if (intHour < 23)
intHour = intHour += intStep;
lblTimerHour.Text = intHour.ToString("00");
}
private void btnHUp_MouseUp(object sender, MouseEventArgs e)
{
t.Abort();
}
}
That gets me InvalidOperationException was unhandled on the
lblTimerHour.Text = intHour.ToString("00");
line. I read what that means and... it might as well be in Mandarin, I kind of get the general concept-ish of what's going wrong, but it's painfully fuzzy. If you asked me the first step in fixing it I'd look at you like a deer in the headlights. We just haven't gotten that far in my class yet.
The problem here is that the label you are trying to update is owned by the main thread (i.e. what the UI runs on), and that means that only that thread can access/update it. So, since you are in a different thread, you need to tell the UI thread to update the label for you.
Something like this would work:
Action updateLabel = () => lblTimerHour.Text = intHour.ToString("00");
lblTimerHour.BeginInvoke(updateLabel);
What this does is tell the lblTimerHour to invoke the action you define above (updateLabel).
See this post: How to update the GUI from another thread in C#?
lblTimerHour.Invoke((MethodInvoker)delegate {
//Do what you need to do with the label
lblTimerHour.Text = intHour.ToString("00");
});
Edit
This should do the trick:
public void dothis()
{
do
{
if (intHour < 23)
intHour = intHour += intStep;
lblTimerHour.Invoke((MethodInvoker)delegate {
//Update the label from the GUI thread
lblTimerHour.Text = intHour.ToString("00");
});
//Pause 1 sec. Won't freeze the gui since it's in another thread
System.Thread.Sleep(1000);
}while(true); //Thread is killed on mouse up
}
Well, let's take a look and see what you already have.
First, I see you did this.
private void btnHUp_MouseDown(object sender, MouseEventArgs e)
{
ThreadStart HUp = new ThreadStart(dothis);
t = new Thread(HUp);
t.Start();
}
While this certainly is not the freshest stuff around it will still work. If you wanted some fresher ingredients then you might go with this instead.
private void btnHUp_MouseDown(object sender, MouseEventArgs e)
{
Task.Factory.StartNew(dothis);
}
Second, I see this.
public void dothis()
{
if (intHour < 23) intHour = intHour += intStep;
lblTimerHour.Text = intHour.ToString("00");
}
The problem here is that you are attempting to update a UI control from a thread other than the main UI thread. You see UI controls have what is called thread affinity. They can only ever be accessed from the thread that created them. What you have will lead to all kinds of unpredictable problems up to and including tearing a whole in spacetime.
A better option would be to do this.
public void dothis()
{
while (intHour < 23)
{
intHour = intHour += intStep;
lblTimerHour.Invoke((Action)(
() =>
{
lblTimerHour.Text = intHour.ToString("00");
}));
}
}
I assumed that you were missing the loop so I added it. While I cannot say that I personally have a taste for this kind of thing it is much easier to swallow. The real problem here is that the worker thread really does not do a whole lot of useful work. And then to top it off we have to use an awkward marshaling operation to transfer the result back to the UI thread. It is not pretty, but it will work.
And finally that brings me to this.
private void btnHUp_MouseUp(object sender, MouseEventArgs e)
{
t.Abort();
}
You are attempting to abort a thread which is highly inadvisable. The problem is that it yanks control from the thread at unpredictable times. That thread might be in the middle of a write to data structure which would corrupt it. This is actually a pretty bad problem because any data structure in the process of being manipulated from any one of the frames on the call stack could be in an inconsistent state. This includes code you did not write. That is why it is hard to say what you may or may not be corrupting by doing this.
What you need to consider instead is using the cooperative cancellation mechanisms. This includes the use of CancellationTokenSource and CancellationToken. Here is how it might look once we put everything together.
private CancellationTokenSource cts = null;
private void btnHUp_MouseDown(object sender, MouseEventArgs e)
{
cts = new CancellationTokenSource();
Task.Factory.StartNew(() => dothis(cts.Token));
}
private void btnHUp_MouseUp(object sender, MouseEventArgs e)
{
cts.Cancel();
}
public void dothis(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
intHour += intStep;
lblTimerHour.Invoke((Action)(
() =>
{
lblTimerHour.Text = intHour.ToString("00");
}));
Thread.Sleep(1000);
}
}
What this does is signal that the worker thread should gracefully shutdown on its own. This gives the worker thread a chance to tidy things up before eventually terminating itself.
If you want to update the UI every X period of time then there are already existing tools for this; a Timer will do exactly what you want, and it will be much more efficient and easier to code than creating a new thread that just spends most of its time napping. Additionally, aborting threads is a very bad sign to see. Avoid it at all costs.
First create the timer and configure it in the constructor:
private System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
private int hour = 0;
private int step = 0;
public Form1()
{
InitializeComponent();
timer.Tick += timer_Tick;
timer.Interval = 1000;
}
Have the Tick event do whatever should be done whenever it ticks.
private void timer_Tick(object sender, EventArgs e)
{
if (hour < 23)
{
hour += step;
lblTimerHour.Text = hour.ToString("00");
}
}
Then just start the timer when you want it to start ticking and stop the timer when you want it to stop:
private void btnHUp_MouseDown(object sender, MouseEventArgs e)
{
timer.Start();
}
private void btnHUp_MouseUp(object sender, MouseEventArgs e)
{
timer.Stop();
}
The timer will automatically ensure that the Tick event handler runs in the UI thread, and it won't block the UI thread (or any other thread) when its waiting for the next event to happen, it will just do nothing.
I have a timer of 1 second in C#, with a while sequence in it. My question is if the while sequence is not finished before 1 second, will the timer tick, and restart the while from the beginning?
The part of the code is below, and what it does is that it cycles through the selected objects and changes something. So, if there are a lot of objects selected and I need more than 1 second to change them, will they all be changed?
P.S. I actually want the loop to be broken; a large number of objects will be selected only by mistake, but I just want to be sure that I avoid this possibility. :)
private void timer1_Tick(object sender, EventArgs e)
{
TSM.ModelObjectEnumerator myEnum = null;
myEnum = new TSM.UI.ModelObjectSelector().GetSelectedObjects();
while (myEnum.MoveNext())
{
if (myEnum.Current != null)
{....}
}
}
Yes, timer ticks can happen concurrently. This means that your timer must be thread-safe.
Except for the UI timer classes (WinForms/WPF). Their tick functions run on the UI thread. With DoEvents you can cause reentrancy even there which is another reason to avoid DoEvents.
From the name of the handler I assume you are using System.Windows.Forms.Timer which is single-threaded. That means the Tick event will fire after the previous one has ended. To break the loop, you will have to execute the code in another thread an use an exit condition.
This is how I usually do it:
private bool running;
private bool restart;
private void DoWork(object item)
{
running = true;
TSM.ModelObjectEnumerator myEnum = null;
myEnum = new TSM.UI.ModelObjectSelector().GetSelectedObjects();
while (myEnum.MoveNext() && !restart)
{
//do your stuff
if (myEnum.Current != null) {....}
}
if(restart)
{
restart = false;
ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork));
}
}
private void timer1_Tick(object sender, EventArgs e)
{
if (running)
restart = true;
else
ThreadPool.QueueUserWorkItem(new WaitCallback(DoWork));
}
A workaround would be to disable the timer at the top of the while event, and re-enable it as you exit the while event.
The while loop will not be broken because the timer has ticked again. But in any case, your best bet would be to disable the timer at the beginning of the event handler, and re-enable it again at the end.
You could always try something similar to this instead, that way you void having multiple timers tick over and kick off processes. Written in Notepad so please excuse any massive spelling mistakes
private Timer _systemTimer = null;
public MyApp()
{
_systemTimer = new Timer("how ever you set your 1 second);
// Create your event handler for when it ticks over
_systemTimer.Elapsed += new ElapsedEventHandler(systemTimerElapsed);
}
protected void systemTimerElapsed(object sender, ElapsedEventArgs e)
{
_systemTimer.Stop();
//Do what you need to do
_systemTimer.Start();
//This way if it takes longer than a second it won't matter, another time won't kick off until the previous job is done
}
I will make it very easy for you;use Thread.Sleep() in another background thread and it is done!
If you know when are you finish than just use AutoResetEvent to keep threads in sync.
If you do not have any control on the update no callback , time is unknown I suggest to increase your timer interval!
var thread = new Thread((ThreadStart)delegate
{
While(true)
{
TSM.ModelObjectEnumerator myEnum = null;
myEnum = new TSM.UI.ModelObjectSelector().GetSelectedObjects();
while (myEnum.MoveNext())
{
if (myEnum.Current != null)
{....}
}
Thread.Sleep(1000);
}
}
thread.Start();
Get each char from string from txtString and write on label one by one char with timerControl
int g = 0;
private void timerString_Tick(object sender, EventArgs e)
{
string a = txtString.Text;
int em = txtString.TextLength;
if (g < em)
{
lblString.Text = lblString.Text + a[g];
g++;
}
else timerString.Stop();
}
Call from
private void btnStringStart_Click(object sender, EventArgs e)
{
timerString.Start();
lblString.Text = "";
}