Event being increasingly, repeatedly fired - c#

I have an unusual thing happening with an RSS presenter I'm trying to make. It's meant to go to the next item after an 'Out' animation is played, then play the 'In' animation. http://oeis.org/A000217
void _timer_Tick(object sender, EventArgs e)
{
Storyboard sbOut = this.FindResource("sbAnimateOut") as Storyboard;
sbOut.Completed += new EventHandler(sbOut_Completed);
sbOut.Begin();
}
void sbOut_Completed(object sender, EventArgs e)
{
if (_selected < _total)
{
_selected++;
}
else
{
GetFeed(_feed);
_selected = 0;
}
lstbxItems.SelectedIndex = _selected;
counter.Text = _selected.ToString();
Storyboard sbIn = this.FindResource("sbAnimateIn") as Storyboard;
sbIn.Begin();
}
I noticed it seem to skip items though. When I step through it line by line, it seems to execute the void sbOut_Completed(object sender, EventArgs e) once the first time, three times the second time, six times the third time - and so on, sequentially.
Perhaps I'm going about this the wrong way and that's causing the issue? Any suggestions?

You are adding another event handler every timer tick!
Move this code:
sbOut.Completed += new EventHandler(sbOut_Completed);
into your initialization - only do it once.

Related

Title show while timer is enabled

so I have a problem where I want to make a label pop up when timer is enabled. I tried writing this but it just doesn't work. I have set every object property as needed, but still there is some kind of problem. Can you help me please? Thanks.
private void timer1_Tick(object sender, EventArgs e)
{
if (button3Click == true && FullNameBOX.Text == "")
{
timer1.Start();
while(timer1.Enabled == true)
{
label5.Show();
}
timer1.Stop();
}
else if(timer1.Enabled == false)
{
label5.Hide();
}
else
{
}
timer1_Tick is only executed once the timer is started and the timer interval is elapsed for the first time and then repeatedly every interval. So you must start the timer somewhere else. In button button3_Click I assume
private void button3_Click(object sender, EventArgs e)
{
if (FullNameBOX.Text == "")
{
label5.Show();
timer1.Start();
}
else
{
... process the FullNameBOX.Text
}
}
In the Tick event handler stop the timer and hide the label
private void timer1_Tick(object sender, EventArgs e)
{
timer1.Stop();
label5.Hide();
}
Also, give better names to your controls. It's best to do so before creating the event handlers so that those get better names too. It is easier to understand messageLabel than label5 and SaveButton_Click than button3_Click.

Need help on my alarm program

I want to make an alarm program where the user is asked to choose the time and the day of the week. If the user choose the day, the text in the label will be in bold. I am having trouble to pass the list from button2_click to Timer_Elapsed. The program worked well except when i pressed the button, the functions in Timer_Elapsed wont work.
List<string> list = new List<string>();
private void Form1_Load(object sender, EventArgs e)
{
timer = new System.Timers.Timer();
timer.Interval = 1000;
timer.Elapsed += Timer_Elapsed;
}
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
{
DateTime currentTime = DateTime.Now;
DateTime userTime = dateTimePicker1.Value;
foreach(string _list in list)
{
if(currentTime.DayOfWeek.Equals(_list) && currentTime.Hour==userTime.Hour && currentTime.Minute==userTime.Minute && currentTime.Second == userTime.Second)
{
SoundPlayer player = new SoundPlayer();
player.SoundLocation = #"C:\Users\Andrew\Music\test1.wav";
player.PlayLooping();
if(MessageBox.Show("","It's Time!", MessageBoxButtons.OK)==DialogResult.OK)
{
player.Stop();
}
}
}
}
private void button2_Click(object sender, EventArgs e)
{
timer.Start();
if (label3.Font.Bold)
{
list.Add("Monday");
}
if (label4.Font.Bold)
{
list.Add("Tuesday");
}
if (label5.Font.Bold)
{
list.Add("Wednesday");
}
if (label6.Font.Bold)
{
list.Add("Thursday");
}
if (label7.Font.Bold)
{
list.Add("Friday");
}
if (label8.Font.Bold)
{
list.Add("Saturday");
}
if (label9.Font.Bold)
{
list.Add("Sunday");
}
foreach (string _list in list)
{
label10.Text = label10.Text + _list + " ";
}
label10.Visible = true;
}
private void label3_Click(object sender, EventArgs e)
{
label3.Font = new Font(label3.Font, FontStyle.Bold);
}
private void label4_Click(object sender, EventArgs e)
{
label4.Font = new Font(label4.Font, FontStyle.Bold);
}
private void label5_Click(object sender, EventArgs e)
{
label5.Font = new Font(label5.Font, FontStyle.Bold);
}
private void label6_Click(object sender, EventArgs e)
{
label6.Font = new Font(label6.Font, FontStyle.Bold);
}
private void label7_Click(object sender, EventArgs e)
{
label7.Font = new Font(label7.Font, FontStyle.Bold);
}
private void label8_Click(object sender, EventArgs e)
{
label8.Font = new Font(label8.Font, FontStyle.Bold);
}
private void label9_Click(object sender, EventArgs e)
{
label9.Font = new Font(label9.Font, FontStyle.Bold);
}
private void button1_Click(object sender, EventArgs e)
{
label3.Font = new Font(label3.Font, FontStyle.Regular);
label4.Font = new Font(label4.Font, FontStyle.Regular);
label5.Font = new Font(label5.Font, FontStyle.Regular);
label6.Font = new Font(label6.Font, FontStyle.Regular);
label7.Font = new Font(label7.Font, FontStyle.Regular);
label8.Font = new Font(label8.Font, FontStyle.Regular);
label9.Font = new Font(label9.Font, FontStyle.Regular);
}
In if statement, You are comparing DayOfWeek Enum value to string: currentTime.DayOfWeek.Equals(_list) this will be always false. Change with this:
currentTime.DayOfWeek.ToString().Equals(_list)
Just like Giorgi said, you are comparing the Enum to a string. You would need to ToString() it before.
As for the rest of your code, here are a couple pointers to make it better overall, since you seem to be beginning. If you don't understand one of the tips, try to read about it. Understanding why it is better is much more important than just blindly changing it. Most of the tips are extremely simplified pointers, and the level is pretty basic too.
Better functionality:
Make a label click either turn it bold or turn it back to normal, depending on it's current state. This will allow a user to remove one of them easily. This will make button1 irrelevant too.
label3.Font = new Font(label3.Font, label3.Font.Bold ? FontStyle.Regular : FontStyle.Bold);
Allow button2 to deactivate the alarm, by clicking it again.
Do not hard-code the path to your sound, instead include it as a ressource, or just put it in the folder with the executable and use a relative path
Nobody needs an alarm up to the second. Make the timer Interval about 30 seconds or so (to avoid millisecond errors), and compare up to the minute.
Don't start the alarm if it is already ringing
Safeguard your dangerous code, like finding the sound file, either by checking it exists beforehand (and reacting appropriately) or with a simple try catch, so that your program doesn't crash at critical moments.
Better code:
Add Enum values (from DayofWeek) to your list, then you will not need to convert anything to compare them
Name your form controls and variables better, so that their use is obvious from the name. "label3", "button2", "list" are horrible names for variables, especially global ones.
Considering the fact that you don't give any other option than "OK" when it rings, don't test the dialog response at all.
player.PlayLooping();
MessageBox.Show("", "It's time!");
player.Stop();
Use the same function for all label clicks, like this:
private void labelDay_Click(object sender, EventArgs e)
{
Label currentLbl = (label) sender;
currentLbl.Font = new Font(currentLbl.Font, currentLbl.Font.Bold ? FontStyle.Regular : FontStyle.Bold);
}
Hold all your labels in an array, so that you can loop through them in the button clicks
Compare the time using a difference, so that you don't have to compare days, then hours, then minutes, etc. A DateTime can easily be compared in one operation.
WinForms has a timer component that you can use from the editor
Dispose of your SoundPlayer when you're done with it, or re-use it. C# takes care of that for you behind the scenes, but that is a very good habit to learn in programming in general
That should be a good start to learning a few very useful things about C# and WinForms, and starting to use better code practices. This is in no way an exhaustive list of everything that could be improved, but since you probably don't plan on ever turning this program into a piece of software used by the whole world, this should be a good stepping stone for you.

Winform application Timer doesn´t work correctly

i´m doing a winform app, and the timer isn´t working fine. First time works, and then, it doesn´t.
Here is the code:
public void GetNewTurn(Turn turn)
{
_tmrStarTime = DateTime.Now;
timer1.Start();
timer1.Tick += tmr1_Tick;
}
private void tmr1_Tick(object sender, EventArgs e)
{
//timer code here
timer.stop();
}
So, the idea is:
GetNewTurn is a function which is invoked from another place. The first time which I invoke it, works fine, then doesn´t. I put a breakpoint inside the tmr1_Tick, and i can see that it just works the first time, then, doesn´t.
In the Timer properties, i set Enable = True.
What i´m doing wrong?
Thanks!
You shoudn't stop the tmr1_Tick in the first tick
public void GetNewTurn(Turn turn)
{
_tmrStarTime = DateTime.Now;
timer1.Start();
timer1.Tick += tmr1_Tick;
}
private void tmr1_Tick(object sender, EventArgs e)
{
//the code for each tick
}
Only add the handler once in the constructor or OnLoad override.
timer1.Tick += tmr1_Tick;
public void GetNewTurn(Turn turn)
{
_tmrStarTime = DateTime.Now;
timer1.Start();
}

Refresh timer and warning boxes in c#

Basically, I have a frequency that refreshes data on screen. When one of these data points goes over a set value, it sets off an error. Upon this error setting off, I want the background colour to change (like a flashing warning).
The problem I have is that I am already using a timer, and when I call a new timer (for the flash) it stops the other timer working, and I'm unaware of how to call in the previous method (being as it uses object sender)
Here is my code:
public void Freq_Change(object sender, SelectionChangedEventArgs e)
{
_timer.Stop();
_timer.Interval = TimeSpan.FromSeconds(Freq.SelectedIndex + 1);
_timer.Start();
_timer.Tick += timer_Tick;
}
and timer_Tick
void timer_Tick(object sender, EventArgs e)
{
//Data generator
//Value pushes to text boxes
if (value is over 100)
{
Warning_Blink
"Oh no, an error"
}
else
{
"All good"
}
Warning_Blink has the new timer in, which then calls warning_Tick
In warning_Tick
private bool _warning = false;
private void warning_Tick(object sender, EventArgs e)
{
if (_warning)
{
ErrorBox.Background = new SolidColorBrush(Colors.Red);
}
else
{
ErrorBox.Background = new SolidColorBrush(Colors.White);
}
_warning = !_warning;
Freq_Change();
}
Here where I call Freq_Change (which doesn't work) I want to be able to go back to the old timer (or better yet never switch between the two) so the data generation can continue.
Can anyone help me with this? I've been scratching my head for hours

Disabling Timer .. why I can't?

When I use System.Windows.Forms.Timer class and finish using it then I can't disable it.. it ticks even if I set its property Enabled to false. What is wrong with the code? here is an example:
int counter = 0;
private void timer1_Tick(object sender, EventArgs e) {
MessageBox.Show("Hello");
counter++;
if (counter == 10){
timer1.Enabled = false;
}
}
This is a subtle bug that's induced by the MessageBox.Show() call. MessageBox pumps a message loop to keep the UI alive. Which allows the Tick event handler to run again, even though it is already active from the previous tick. The counter variable doesn't get incremented until you click the OK button. As a result, the screen fills with message boxes and that won't stop until you click the OK button ten times.
You need to increment the counter before showing the message box. Fix:
int counter = 0;
private void timer1_Tick(object sender, EventArgs e) {
counter++;
if (counter > 10) timer1.Enabled = false;
else MessageBox.Show("Hello");
}
This kind of problem is also the reason that DoEvents() got such a bad reputation. It is pretty difficult to write code that can properly deal with the re-entrancy induced by the message loop. You need to keep boolean flags around that indicate that code is already active. Which is another way to solve your problem:
int counter = 0;
bool showingBox = false;
private void timer1_Tick(object sender, EventArgs e) {
if (showingBox) return;
showingBox = true;
try {
MessageBox.Show("Hello");
counter++;
if (counter == 10) timer1.Enabled = false;
}
finally {
showingBox = false;
}
}
You now get only one message box at a time, probably what you are really looking for.
I should mention that this re-entrancy problem is pretty specific to timers. A dialog takes counter-measures to avoid re-entrancy problems, it disables all the windows in the application. That ensures that the user cannot do things like closing the main window or clicking a button that brings up the dialog again. Both rather disastrous mishaps. That takes care of most of the 'unexpected' Windows notifications, basically any of the messages that are generated by the user. The edge case is a timer (WM_TIMER is not disabled) whose event handler has a UI effect.
It is because the MessageBox.Show blocks until the user presses OK.
The code below the MessageBox will not execute until after 10 OK buttons are pressed.
But the timer continues to fire even if the execution is blocked.
Try this code
int counter = 0;
private void timer1_Tick(object sender, EventArgs e) {
counter++;
if (counter == 10){
timer1.Enabled = false;
}
MessageBox.Show("Hello");
}
(just moved the MessageBox)
What about timer1.Stop()? I am not too familiar with this class, but looked it up quickly: Timer Class
try this:
int counter = 0;
private void timer1_Tick(object sender, EventArgs e) {
MessageBox.Show("Hello");
counter++;
if (counter == 10){
timer1.Stop();
timer1.Dispose();
timer1 = null;
}
}
It is also working for me. I placed the timer in the form, on the button click, I am calling timer1.start() and I put the following code in the tick event and its working.
int i = 0;
private void timer1_Tick(object sender, EventArgs e)
{
i++;
this.Text = i.ToString();
if (i == 10)
{
timer1.Enabled = false;
}
}
You need to call Stop.
int i = 0;
private void timer1_Tick(object sender, EventArgs e)
{
i++;
if (i == 10)
{
timer1.Stop();
}
}
I'm pretty sure the MessageBox is the culprit here. Maybe if you use a short execution interval for the timer handler then this could potentially cause your code to function undesirably, if executions are overlapping.
The problem would be, in this case, that the handler executes and displays the MessageBox which in turn halts execution of the current scope until the the prompt is acknowledged by the user, meanwhile the handler has started again, showing another prompt, and another, and so on. At this point, we have multiple MessageBoxes waiting for input, yet counter hasn't even been incremented once. When we click 'OK' on the prompt, counter increments as desired, but at this point has a value of 1, rather than a value representing the number of prompts shown. This means yet another prompt will be displayed if more time elapses, until the user clicks 'OK' on at least 10 prompts.
You could try inhibiting execution if the process is already under way in order to prevent concurrent runs:
private readonly ReaderWriterLockSlim Locker = new ReaderWriterLockSlim();
int counter = 0;
private void timer1_Tick(object sender, EventArgs e)
{
if (Locker.TryEnterWriteLock(0))
{
try
{
MessageBox.Show("Hello");
Counter++;
if (Counter == 10)
{
Timer.Enabled = false;
}
}
catch { }
finally
{
Locker.ExitWriteLock();
}
}
}

Categories