C# refreshing a chart every few seconds - c#

I have a Windows Forms App written in C#. The idea is, that it draws a chart for 10 numbers after clicking a button. This works fine. I click the button, and I get a nice chart. However I also want to include a sort of "auto refresh" mode, where the chart is refreshed every few seconds. This would be enabled via Checkbox. Here's my code:
private void chartButton_Click(object sender, EventArgs e) //draw a chart after the button is clicked
{
Random rdn1 = new Random();
int value;
foreach (var series in ekran.Series) //clear previous values
{
series.Points.Clear();
}
for (int i = 0; i < 10; i++) //draw a chart from ten new values
{
value = rdn1.Next(0, 10); //for testing purpouses the value will be a random number a random number
ekran.Series["seria1"].Points.AddXY(i, value);
}
}
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
while(checkBox1.Checked) //click the chartButton every one second, when the checkbox is checked
{
//rysuj.PerformClick();
chartButton.PerformClick();
Thread.Sleep(1000);
}
}
And now for my problem. When I check the Checkbox, I will not get a chart until it finishes every iteration of the while loop. Since it's an infinite loop, I will never get my chart. If I rewrite the code to make only five iterations when the Checkbox is checked, I only get the chart for the fifth one (and after five seconds, as to be expected).
So my question is: how can I force this to draw a chart every time the button is clicked via chartButton.PerformClick()? When I click the button manually, everything works fine, it's just when I try to do it automatically, I get my problem.
EDIT
First of all,thank you for the replies. However, I'm still experiencing the same problem when using a timer. This is how my code looks now:
namespace ChartTest
{
public partial class Form1 : Form
{
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
public Form1()
{
InitializeComponent();
timer.Tick += new EventHandler(timer_Tick);
timer.Interval = 1000;
}
void timer_Tick(object sender, EventArgs e)
{
timer.Enabled = false;
chartButton.PerformClick();
}
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
while (checkBox1.Checked)
{
timer.Enabled = true; // Enable the timer
timer.Start(); // Start the timer
}
}
private void chartButton_Click(object sender, EventArgs e) //draw a chart after the button is clicked
{
Random rdn1 = new Random();
int value;
ekran.Series.Clear();
var series2 = new System.Windows.Forms.DataVisualization.Charting.Series
{
Name = "Series2",
Color = System.Drawing.Color.Green,
IsVisibleInLegend = false,
IsXValueIndexed = true,
ChartType = SeriesChartType.Line
};
this.ekran.Series.Add(series2);
for (int i = 0; i < 100; i++)
{
value = rdn1.Next(0, 10);
series2.Points.AddXY(i, value);
}
}
}
}
Sorry for being a total noob, but I have no idea, what am I doing wrong this time.

This is exactly what a Timer is for. Have the checkbox start/stop or enable/disable the timer, and handle the Timer.Tick event to redraw your chart. In your case, the event handler could simply call chartButton.PerformClick(), or insert whatever code the PerformClick() does.
ETA: If the chart refresh is not instant, you will probably want to push it off to a separate thread. If it's instant, there's not really any need to deal with the threading though.

I would go the route of using a thread with combination of checkbox's checkChange() event. Essentially this will allow your application to keep running while the update code will execute periodically. The refresh is determined by the sleep time, not your manual click or any other value.. Example below on how I to do this:
Thread refreshThread = null;
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
if (refreshThread == null) //No thread running, assume it starts this way
{
refreshThread = new Thread(chartRefresh);
refreshThread.Start();
}
else //Thread is running, must terminate
{
refreshThread.Abort();
refreshThread = null;
}
}
private void chartRefresh()
{
while (true)
{
//code to refresh chart
Thread.Sleep(10000);
}
}

Related

C# Using WebClient & a Timer Together To Actively Display Updated Data

I have a site that is extremely basic and will only ever consist of a single integer.
However the integer will actively change, I want to add onto my existing application to display what this integer is in real time.
-I've tried using a Timer and WebClient however if I put the code under InitializeComponent() the form will never load.
-Also if I put the code in Form1_Load the form will never load.
-I was successful in getting the number to display in real time by putting the code under a button_click event, but I want this code to begin as soon as the form load.
-Also when the button was first clicked the first timer sequence the label would display lat (unsure what this means)
-After the button was pressed and the timer loop began the app breaks, the number will update properly, but you cannot use any other functionality of the app, you can not move the window, you cannot close the app, etc..
private void timer_Tick(object sender, EventArgs e)
{
Timer timer = (Timer)sender;
this.Visible = false;
timer.Stop();
this.Visible = true;
}
private void button1_Click(object sender, EventArgs e)
{
int c = 5;
while (c == 5)
{
using (var client = new WebClient())
{
var s = client.DownloadString(#"myURL.html");
var htmldoc2 = (IHTMLDocument2)new HTMLDocument();
htmldoc2.write(s);
var plainText = htmldoc2.body.outerText;
label1.Text = plainText;
System.Windows.Forms.Timer timer = new System.Windows.Forms.Timer();
timer.Interval = 5000;
timer.Tick += new EventHandler(timer_Tick);
timer.Start();
}
}
}
Please help me no clue what I am doing wrong here
I managed to fix my issue by using the following code if anyone ever has a similar question:
private void test()
{
using (var client = new WebClient())
{
var s = client.DownloadString(#"myURL.html");
var htmldoc2 = (IHTMLDocument2)new HTMLDocument();
htmldoc2.write(s);
var plainText = htmldoc2.body.outerText;
label1.Text = plainText;
}
}
int i = 1;
private void timer1_Tick(object sender, EventArgs e)
{
i += 1;
if (i >= 199)
{
i = 1;
timer1.Stop();
timer1.Start();
}
test();
}
timer1 was added to the winform from the toolbox, and is set to enabled with an interval of 200

Reset button in a countdown timer

I have a button, which I press and it starts a countdown.
But, if I press the same button again, the timer must reset and do another countdown (with another time defined by my program, but now this is irrelevant).
Is there any way I can do this reset inside the same button_click?
Maybe checking if the button was clicked again so I can reset the timer values?
I have this timer tick
private int milliSecondsLeft = 0;
private int t = 0;
private bool timeSet = false;
private void timer2_Tick(object sender, EventArgs e)
{
string timeOp = dataGridView1.Rows[t].Cells[5].Value + "";
t++;
DateTime timeConvert;
DateTime dateTime = DateTime.Now;
if (!timeSet)
{
DateTime.TryParse(timeOp, out timeConvert);
milliSecondsLeft = (int)timeConvert.TimeOfDay.TotalMilliseconds;
timeSet = true;
timeSetNxt = false;
}
milliSecondsLeft = milliSecondsLeft - 1000;
if (milliSecondsLeft > 0)
{
var span = new TimeSpan(0, 0, 0, 0, milliSecondsLeft);
lblLeft.Text = span.ToString(#"hh\:mm\:ss");
}
else
{
timer2.Stop();
}
}
and this button_click
each time I press my button it goes t++;, then it reads another time value on my datagrid. thats why it must reset
int t = 1;
private void btn2_Click(object sender, EventArgs e)
{
timer2.Start();
lblLeft.Text = dataGridView1.Rows[t].Cells[5].Value.ToString();
string value = dataGridView1.Rows[t].Cells[5].Value.ToString();
lblLeft.Text = value.ToString();
t++;
}
You could use the Tag property of the Button to set a flag for that logic you want to create.
on the button click event
if (btnExample.Tag==0)
{
btnExample.Tag=1;
//call startCountDown function
}
else
{
btnExample.Tag=0;
// call reset
}
Show your Timer Code. To get the Number of resets. Use code below.
int button_clicked = new int();
private void button1_Click(object sender, EventArgs e)
{
// How many times you have Reset
button_clicked++;
// Your Timer Code
}
Just start a new Timer with Every click. Also, dispose the last one.
You can use button_clicked to know if a timer has been started and hence dispose if the button_clicked > 0
I would check if the timer is enabled
if (!timer2.Enabled) StartTimer2();
else ResetTimer2();

C# Using timer1_Tick to plot FastLine chart in realtime

I am coding in C#, and I have 3 variables that are being updated via my timer every 1000ms.
I want to use this timer to have a FastLine chart that plots the new points every 1000ms obviously.
I have got this working to a certain extent.
It is plotting each tick the timer does, but it just keeps adding to it i only want it to show the previous 20 plots, not the past 2000 if the program has been running that long.
My code below for the chart within my timer1_Tick method:
try
{
chart1.Series[0].Points.AddXY(xaxis++, CPUTemperatureSensor.Value);
chart1.Series[1].Points.AddXY(xaxis, NvdGPUTemperatureSensor.Value);
chart1.Series[2].Points.AddXY(xaxis, ramusedpt);
}
catch
{
}
xaxis is declared previously as an int, no need to show all code as rest is irrelevant
Below code is what was used to solve the problem, posted by the user jstreet
Link to the hread and his comment:
How to move x-axis grids on chart whenever a data is added on the chart
public partial class Form1 : Form
{
Timer timer;
Random random;
int xaxis;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
random = new Random();
timer = new Timer();
timer.Interval = 1000;
timer.Tick += timer_Tick;
timer.Start();
}
void timer_Tick(object sender, EventArgs e)
{
chart1.Series[0].Points.AddXY(xaxis++, random.Next(1, 7));
if (chart1.Series[0].Points.Count > 10)
{
chart1.Series[0].Points.Remove(chart1.Series[0].Points[0]);
chart1.ChartAreas[0].AxisX.Minimum = chart1.Series[0].Points[0].XValue;
chart1.ChartAreas[0].AxisX.Maximum = xaxis;
}
}

Stopwatch changing button text in c#

Basically, I've got multiple button in my Form, and I want for it show a Stopwatch in the button.Text when the button is pressed. (Button is modified to be a toggle button.) and to stop and reset the timmer when the button is toggled off. Simple enough it seemed but because I have multiple buttons that could be pressed in any order, and I don't know anything about threading, this seems to be much more difficult that I presumed.
My origional intent was to have a function that constantly runs every second and interates a interager only if the button is pressed using this code:
public void Jogger()//purpose is to step up time[0] every second only when a button is on.
{
while (true)
{
for (int i = 0; i < 16; i++)
{
if (btnstat[i])
time[i]++;
}
Thread.Sleep(1000);
}
}
Problem is, I don't know threading so when I call the function, its stuck doing this and only this.
Either way, once this is called, all i do us call my update function that updates all the buttons including the button.Text which displays the time[0]; (array built around buttons)
Is their a better way of doing this that doesn't cause so much CPU use and/or simply works?
Thanks for all the help!
-John Ivey
Assuming you using checkbox with property Button = Appearence, in event handler for CheckedChanged:
private void CheckBoxCheckedChanged(object sender, EventArgs e)
{
CheckBox checkBox = (CheckBox) sender;
if (checkBox.Checked)
{
Timer timer = new Timer {Interval = 1000};
timer.Tick += Jogger;
timer.Start();
timer.Tag = new CheckboxCounter {CheckBox = checkBox, Time = 0};
checkBox.Tag = timer;
}
else
{
Timer timer = checkBox.Tag as Timer;
if (timer != null)
{
timer.Tag = null;
timer.Stop();
timer.Dispose();
checkBox.Tag = null;
}
}
}
Change your Jogger function:
private void Jogger(object a_sender, EventArgs a_eventArgs)
{
Timer timer = (Timer) a_sender;
CheckboxCounter data = (CheckboxCounter)timer.Tag;
data.Time++;
data.CheckBox.Text = data.Time.ToString();
}
You also need some simple class to store checkbox and current time:
class CheckboxCounter
{
public CheckBox CheckBox;
public int Time;
}
Then you can add any number of checkboxes and just set event CheckedChanged to CheckBoxCheckedChanged.
Try this out. After re-building or running, you should have the new "ButtonTimer" at the top of your ToolBox. Drop a couple on your Form, run it, and see what happens when you click them. Right click them to "Reset" them:
public class ButtonTimer : CheckBox
{
private System.Windows.Forms.Timer Tmr = new System.Windows.Forms.Timer();
private System.Diagnostics.Stopwatch SW = new System.Diagnostics.Stopwatch();
public ButtonTimer()
{
this.Tmr.Interval = 500;
this.Tmr.Tick += new EventHandler(tmr_Tick);
this.Appearance = System.Windows.Forms.Appearance.Button;
this.CheckedChanged += new EventHandler(ButtonTimer_CheckedChanged);
ContextMenuStrip cms = new ContextMenuStrip();
ToolStripItem tsi = cms.Items.Add("Reset");
tsi.Click += new EventHandler(tsi_Click);
this.ContextMenuStrip = cms;
}
protected override void OnLayout(LayoutEventArgs levent)
{
base.OnLayout(levent);
this.Text = TimeSpan.Zero.ToString(#"hh\:mm\:ss");
}
private void ButtonTimer_CheckedChanged(object sender, EventArgs e)
{
if (this.Checked)
{
this.SW.Start();
this.Tmr.Start();
}
else
{
this.SW.Stop();
this.Tmr.Stop();
}
}
private void tmr_Tick(object sender, EventArgs e)
{
this.UpdateTime();
}
private void UpdateTime()
{
this.Text = this.SW.Elapsed.ToString(#"hh\:mm\:ss");
}
private void tsi_Click(object sender, EventArgs e)
{
if (this.SW.IsRunning)
{
SW.Restart();
}
else
{
SW.Reset();
}
this.UpdateTime();
}
}
Application.DoEvents() for simplicity put inside loop . . but it is advisable to start to lean threading . you will just learn how to start thread and how make cross thread safe call
Next simple will be to use backgroundworker . look this http://msdn.microsoft.com/en-us/library/system.componentmodel.backgroundworker.aspx
ok here is thread solution also as you wanted . Tested too . as a stop variable i used Tag. But u can inherit button to make state button.it be more clear way . And below code will use one thread per button . So u should make it in one thread to make it better solution . You can modify this code to do all checkings inside one thread . For this you start thread once can make delegate for attaching dinamically count function for each button or you can pass buttons before . With one word there are more than one way to do it. Good luck
this.button1.Click += new System.EventHandler(this.button_Click);
this.button2.Click += new System.EventHandler(this.button_Click);
...and so on
private void button_Click(object sender, EventArgs e)
{
Thread x= new Thread(new ParameterizedThreadStart(Jogger2));
x.Start(sender);
}
private void button_Click(object sender, EventArgs e)
{
Button mybtn=sender as Button;
if((string)mybtn.Tag=="start"){
mybtn.Tag ="";
return;
}
mybtn.Tag = "start";
Thread x= new Thread(new ParameterizedThreadStart(Jogger2));
x.Start(sender);
}
private bool setResult(object obj,string text)
{
if (this.textBox1.InvokeRequired)
{
Func<Button,string, bool > d = new Func<Button,string,bool >(setResult);
return (bool)this.Invoke(d,obj,text);
}
else
{
Button btn=obj as Button;
if (btn != null)
{
btn.Text = text;
if ((string)btn.Tag !="start") return false;
}
return true;
}
}
private void Jogger2(object mybtn)
{
int ii = 0;
while (true)
{
Thread.Sleep(1000);
//replace with your code
ii += 1;
if (!setResult(mybtn, ii.ToString())) break;
}
}

Graph dynamic update

I have an application that acquires temperature data from the serial port every time that the temperature changes. The value get stored in a variable and is shown in a text box.
I would like to show the temperature versus time in a graph.
I have setup the graph as following but I don't know where to start to get the temperature line updated versus time every second.
InitializeComponent();
//Chart
chart1.ChartAreas.Add("areas");
chart1.ChartAreas["areas"].AxisX.Minimum = 0;
chart1.ChartAreas["areas"].AxisX.Interval = 1;
chart1.ChartAreas["areas"].AxisY.Minimum = 0;
chart1.ChartAreas["areas"].AxisY.Maximum = 250;
chart1.ChartAreas["areas"].AxisY.Interval = 10;
chart1.ChartAreas["areas"].AxisX.Title = "Time [s]";
chart1.ChartAreas["areas"].AxisY.Title = "Temperature [°C]";
chart1.Series.Add("Temperature");
chart1.Series["Temperature"].Color = Color.Red;
chart1.Series["Temperature"].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
chart1.Titles.Add("Extruder Temperature");
//this two lines are only to see something in the graph
chart1.Series["Temperature"].Points.AddXY(0, 20);
chart1.Series["Temperature"].Points.AddXY(1, 50);
}
I think I need to update the "point" and the "value" (0, 20) by replacing the values with the variables but if I do so, I have only one point in the graph and I cannot show the previous values.
Being a beginner I would appreciate some help.
An alternative way (to refresh every second) is to use a BackgroundWorker. This question give an example. Essentially, you start a background thread that updates the view every set amount of time.
private BackgroundWorker _bw;
....
InitializeComponent();
_bw = new BackgroundWorker
{
WorkerReportsProgress = true,
WorkerSupportsCancellation = true
};
_bw.DoWork += bw_DoWork;
_bw.ProgressChanged += bw_ProgressChanged;
_bw.RunWorkerCompleted += bw_RunWorkerCompleted;
_bw.RunWorkerAsync ("Hello to worker");
if (_bw.IsBusy) _bw.CancelAsync();
}
private void bw_DoWork (object sender, DoWorkEventArgs e)
{
if (_bw.CancellationPending) { e.Cancel = true; return; }
var dataPoints = YourConnectToSerialPortAndGetDataFunction();
_bw.ReportProgress (dataPoints);
Thread.Sleep (1000);
e.Result = dataPoints; // This gets passed to RunWorkerCompleted
}
private void bw_RunWorkerCompleted (object sender,
RunWorkerCompletedEventArgs e)
{
}
private void bw_ProgressChanged (object sender,
ProgressChangedEventArgs e)
{
//UPDATE YOUR LABEL
chart1.Series["Temperature"].Points.AddXY(0, 20);
chart1.Series["Temperature"].Points.AddXY(1, 50);
}
After you acquired new data, simply add another Datapoint:
Just to give you an idea (as you haven't posted your data receive method):
OnSerialReceive()
{
var temp = serialport.read();
var time = (int)stopwatch.Elapsed.TotalSeconds;
chart1.Series["Temperature"].Points.AddXY(time, temp);
}
And yes, as soon as you get real data into your diagram, you will want to remove your fake data. Fake data is confusing.

Categories