Increment with timer.Tick not working - c#

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.

Related

C# Visual Studio Form, Can't Reset Timer

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

ComboBox menu repeat bug after typing in textbox in Winform app

I have a primitive app with 2 comboBoxes. They work fine after first starting the app.
However after typing in text in the search bar and pressing enter, the comboboxes loop their contents.
It happens after I type in the textbox, even if I do not press enter. Every time I press a key another repeat list of options appends to the comboBox.
How do I prevent this comboBox malfunction? Here is 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;
//Nick Knapp
//CSCI 363 Fall 2019
namespace c363_hw3_2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
this.Text = "Library Literature Search";
this.BackColor = Color.White;
comboBox2.Items.Add("All");
comboBox2.Items.Add("Books");
comboBox2.Items.Add("Papers");
comboBox2.Items.Add("Films");
comboBox2.Items.Add("CDs");
comboBox2.Items.Add("Other");
comboBox2.SelectedIndex = 0;
comboBox1.Items.Add("Title");
comboBox1.Items.Add("Author");
comboBox1.Items.Add("Publisher");
comboBox1.Items.Add("ISBN");
comboBox1.SelectedIndex = 0;
// this.textBox1.KeyPress += new System.Windows.Forms.KeyPressEventHandler(CheckEnter);
//this.Controls.Add(textBox1);
this.ActiveControl = textBox1;
textBox1.KeyPress += new KeyPressEventHandler(keypressed);
}
private void keypressed(Object o, KeyPressEventArgs e)
{
if (e.KeyChar == (char)Keys.Return)
{
textBox1.Text = "";
e.Handled = true;
}
}
private void tableLayoutPanel1_Paint(object sender, PaintEventArgs e)
{
}
private void comboBox2_SelectedIndexChanged(object sender, EventArgs e)
{
}
}
}
I think your code is correct but have you ever tried these 2 method after calling combo boxes?
comboBox1.ResetText();
comboBox1.Items.Clear();```
i think it works
private void keypressed(Object o, KeyPressEventArgs e)
{
if (e.KeyChar == (char)Keys.Return)
{
textBox1.Text = "";
comboBox1.Items.Clear();
e.Handled = true;
}
}

Application is not responding

What the application should do
This application should take the input of time (seconds, minutes and hours) and shutdown the computer after that time. It should also update the text box with how long left until the computer has shut down.
What the application actually does
I had an issue that I 'fixed' where the called ac across threads weren't safe, so I fixed it and I don't get that error now. However, the updateThread doesn't update and print the time left; and the text box doesn't get "test" appended to it. The UI also becomes Not Responding. Any help would be much appreciated.
Also, if you see anything else that could be done better, please comment and explain. Thanks!
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ShutdownPC
{
public partial class Form1 : Form
{
int inputHours;
int inputMinutes;
int inputSeconds;
Thread sleepingThread;
Thread updatingThread;
NotifyIcon shutdownPCIcon;
Icon defaultIcon;
public Form1()
{
InitializeComponent();
defaultIcon = new Icon("defaultIcon.ico");
shutdownPCIcon = new NotifyIcon();
shutdownPCIcon.Icon = defaultIcon;
shutdownPCIcon.Visible = true;
MenuItem progNameMenuItem = new MenuItem("ShutdownPC by Conor");
MenuItem breakMenuItem = new MenuItem("-");
MenuItem quitMenuItem = new MenuItem("Quit");
ContextMenu contextMenu = new ContextMenu();
contextMenu.MenuItems.Add(progNameMenuItem);
contextMenu.MenuItems.Add(breakMenuItem);
contextMenu.MenuItems.Add(quitMenuItem);
shutdownPCIcon.ContextMenu = contextMenu;
shutdownPCIcon.Text = "ShutdownPC";
quitMenuItem.Click += QuitMenuItem_Click;
}
private void QuitMenuItem_Click(object sender, EventArgs e)
{
shutdownPCIcon.Dispose();
sleepingThread.Abort();
updatingThread.Abort();
this.Close();
}
public void sleepThread()
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(sleepThread));
}
else {
textBox1.Enabled = false;
textBox2.Enabled = false;
textBox3.Enabled = false;
button1.Enabled = false;
int totalMilliseconds = ((inputHours * 3600) + (inputMinutes * 60) + inputSeconds) * 1000;
Thread.Sleep(totalMilliseconds);
//Process.Start("shutdown", "/s /t 0");
richTextBox1.AppendText(String.Format("test"));
}
}
public void updateThread()
{
if (this.InvokeRequired)
{
this.Invoke(new MethodInvoker(updateThread));
}
else {
int totalSeconds = (inputHours * 3600) + (inputMinutes * 60) + inputSeconds;
while (totalSeconds > 0)
{
TimeSpan time = TimeSpan.FromSeconds(totalSeconds);
string timeOutput = time.ToString(#"hh\:mm\:ss");
richTextBox1.AppendText(String.Format(timeOutput));
Thread.Sleep(1000);
richTextBox1.Clear();
totalSeconds--;
}
}
}
private void textBox1_TextChanged(object sender, EventArgs e)
{
inputHours = Convert.ToInt32(textBox1.Text);
inputHours = int.Parse(textBox1.Text);
}
private void textBox2_TextChanged(object sender, EventArgs e)
{
inputMinutes = Convert.ToInt32(textBox2.Text);
inputMinutes = int.Parse(textBox2.Text);
}
private void textBox3_TextChanged(object sender, EventArgs e)
{
inputSeconds = Convert.ToInt32(textBox3.Text);
inputSeconds = int.Parse(textBox3.Text);
}
private void button1_Click(object sender, EventArgs e)
{
updatingThread = new Thread(new ThreadStart(updateThread));
updatingThread.Start();
sleepingThread = new Thread(new ThreadStart(sleepThread));
sleepingThread.Start();
}
private void richTextBox1_TextChanged(object sender, EventArgs e)
{
}
}
}
Using Invoke in the beginning of method that runs in separate thread is bad idea, because all code runs in GUI thread and lock it.
You should Invoke only GUI updating code!!!

My variable is not changing as it is supposed to

My purpose is to change drawlines gradient each second in the form application. However it doesn't work. valueble counter " changing in label but not changing in form paint ..
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;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private int counter = 1;
private void timer1_Tick(object sender, EventArgs e)
{
counter++;
if (counter >= 10)
timer1.Stop();
lblCountDown.Text = counter.ToString();
}
private void button1_Click(object sender, EventArgs e)
{
counter = 0;
timer1.Tick += new EventHandler(timer1_Tick);
counter = new int();
timer1.Interval = 1000; // 1 second
timer1.Start();
lblCountDown.Text = counter.ToString();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawLine(new Pen(Brushes.Crimson),200,200,counter ,300) ;
}
}
}
I intend to change my drawings gradient with time but variable is not changing when its
come to form paint... but it does change in lbl ...
help me if u can guys . dont know what to do.
Here, this one works. The answer is to call this.Invalidate() on form every timer tick.
public partial class Form1 : Form
{
int counter = 0;
public Form1()
{
InitializeComponent();
timer1.Tick += new EventHandler(timer1_Tick);
}
private void button1_Click(object sender, EventArgs e)
{
counter = 1;
timer1.Interval = 1000; // 1 second
timer1.Start();
lblCountDown.Text = counter.ToString();
}
private void timer1_Tick(object sender, EventArgs e)
{
counter++;
if (counter >= 10)
timer1.Stop();
lblCountDown.Text = counter.ToString();
this.Invalidate();
}
private void Form1_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawLine(new Pen(Brushes.Crimson), 200, 200, counter*10, 300);
}
}
Also changed several things:
Event handler is set only once – to avoid multiple handlers if user clicks button several times.
Removed counter = new int() – no need, you have already set it to =1.
In Form1_Paint set x2 coordinate to counter*10 so it is easier to see the movement.
I will recommend following:
Use Panel control for Drawing and its paint event e.g. Panel_Paint
In Timer_Tick use Panel.Invalidate();
In the paint event Dispose for graphic object that is Pen.
Add a Panel control named panel1 in the form.
Keep every other control outside the panel.
Example of Panel Paint and Timer event:
private void panel1_Paint(object sender, PaintEventArgs e)
{
using (Pen pen = new Pen(Brushes.Crimson))
{
e.Graphics.DrawLine(pen, 200, 200, counter, 300);
}
}
private void timer1_Tick(object sender, EventArgs e)
{
counter++;
if (counter >= 10)
timer1.Stop();
lblCountDown.Text = counter.ToString();
panel1.Invalidate();
}

Visual C# Windows Form Textbox Not Auto-updating

I'm attempting to code an application that reads in the values from an IMU. I'm trying to get the different values of the attitude (i.e. direction) of the IMU for 1 second when using the getAtr_Click method. However, while this is calling the Get_Attitude function, it only changes the textbox values once on my form. How do I make it change each time? (I want to see 10 different values flash by on the textbox).
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.Windows.Forms;
using System.Threading;
using System.Timers;
using VectorNav.Devices;
namespace WindowsFormsApplication1
{
public partial class Form1 : Form
{
public static Vn100 vn100 = new Vn100("COM5", 9600);
// New Vn100 on COM5
private void Get_Attitude()
//gets the current yaw, pitch, roll in degrees, and displays
{
var attitude = vn100.CurrentAttitude;
yaw.Text = Convert.ToString(attitude.Ypr.YawInDegs);
pitch.Text = Convert.ToString(attitude.Ypr.PitchInDegs);
roll.Text = Convert.ToString(attitude.Ypr.RollInDegs);
}
public Form1()
//connect to the Vn100, set its output to YPR, output at 10Hz
{
InitializeComponent();
vn100.Connect();
vn100.SetAsyncDataOutputType(Vn100.AsyncOutputType.Ypr, true);
vn100.SetAsyncDataOutputFreq(10, true);
}
private void Form1_Load(object sender, EventArgs e)
{
Get_Attitude();
}
private void tare_Click(object sender, EventArgs e)
{
vn100.Tare(true);
vn100.Tare(true); //for some reason it doesn't display the correct Attitude values w/out a double Tare
Get_Attitude();
}
private void getAtr_Click(object sender, EventArgs e)
{
for (int i = 1; i <= 10; i++)
{
while (vn100.CurrentAttitude == null)
Thread.Sleep(10);
Get_Attitude();
}
}
protected override void OnFormClosing(FormClosingEventArgs e)
//disconnect from the Vn100 when the box is closed
{
vn100.Disconnect();
base.OnFormClosing(e);
}
}
}
This is because you are haulting the UI thread:
private void getAtr_Click(object sender, EventArgs e)
{
for (int i = 1; i <= 10; i++)
{
while (vn100.CurrentAttitude == null)
Thread.Sleep(10); // < - UI can't respond in a sleep
Get_Attitude();
}
}
You could do this:
private void getAtr_Click(object sender, EventArgs e)
{
for (int i = 1; i <= 10; i++)
{
while (vn100.CurrentAttitude == null)
{
Thread.Sleep(10); // < - UI can't respond in a sleep
Application.DoEvents();
}
Get_Attitude();
}
}
Or you could force the update to happen synchronously:
private void tare_Click(object sender, EventArgs e)
{
vn100.Tare(true);
vn100.Tare(true); //for some reason it doesn't display the correct Attitude values w/out a double Tare
Get_Attitude();
this.Update();
}
Or you could use a timeout to update the form instead of a loop.
private void tare_Click(object sender, EventArgs e)
{
System.Windows.Forms.Timer myTimer = new System.Windows.Forms.Timer();
myTimer.Tick += new EventHandler(TimerEventProcessor);
myTimer.Interval = 10;
myTimer.Start();
}
void TimerEventProcessor(object sender, EventArgs e)
{
Get_Attitude();
}
Do you want to see 10 (possibly) different values displayed in your textboxes during 1s, at an interval of 10ms, once you call getAttr_Click()?
If yes, then that would be the way to do it:
private void getAtr_Click(object sender, EventArgs e)
{
for (int i = 1; i <= 10; i++)
{
while (vn100.CurrentAttitude == null)
Thread.Sleep(10);
Get_Attitude();
Thread.Sleep(10);
}
}
In your version, the thread that called the getAttr_Click() method just checks vn100.CurrentAttutude at 10ms intervals. Once that has a non-null value, I suspect it remains non-null, meaning your while() loop will be skipped in every for() iteration so Get_Attitude() will be called 10 times in a row (probably) so fast that you only get to see the last values on the screen.
Thing is, this will keep your UI unresponsive each 10ms during the click, so you might consider calling this getAtr_Click() asynchronously or other trivial solution.
EDIT: Actually, it would help knowing the behaviour of the vn100 component. In the case it is unpredictable, the only thing you can be sure of is displaying 10 different values at no less than 10ms distance in time, regardless if you do everything on the UI thread or on a different thread. It is all related to that vn100.CurrentAttitude behaviour...
change it to:
private void Get_Attitude()
//gets the current yaw, pitch, roll in degrees, and displays
{
var attitude = vn100.CurrentAttitude;
yaw.Text = Convert.ToString(attitude.Ypr.YawInDegs);
pitch.Text = Convert.ToString(attitude.Ypr.PitchInDegs);
roll.Text = Convert.ToString(attitude.Ypr.RollInDegs);
vn100.CurrentAttitude = null;
}
private void getAtr_Click(object sender, EventArgs e)
{
for (int i = 1; i <= 10; i++)
{
while (vn100.CurrentAttitude == null)
Thread.Sleep(10);
Get_Attitude();
Application.DoEvents();
}
}

Categories