Reputation: 1
I'm having a problem. So I've built an app which displays data in the form of chart and a datagridview. They are both responsive. That means they rescale and move with the data. It takes some computation power I guess.
At the same time I have timers cause it all runs periodically with f=4Hz.
And now: When I run the app and switch on the periodical readout the app hangs during resizing. How could I prevent it?
I've already tried to use a backgroundworker, but the problem occurs in the moment of accessing to the datagridview and chart which are declared (and also used) in the "other thread" (as the VS said)
So.. How could I prevent it? Maybe I should utilise the backgroundworker in the other way?
My attempts with the backgroundworker:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
//Control.CheckForIllegalCrossThreadCalls = false;
if (!GetConnectionStatus())
{
stop_ticking();
if (MessageBox.Show("Device not connected", "Connection status", MessageBoxButtons.RetryCancel, MessageBoxIcon.Error) == DialogResult.Retry)
messaging();
else
return;
}
// TEMP READ
Read_temp(tlist);
float[] t = new float[3];
float[] r = new float[3];
float[] av = new float[1];
float[] st = new float[1];
// TEMP IMPORT
tlist.Give_current_temp(t, r, av, st);
string time_stamp = tlist.Give_current_time();
rows_nr++;
// ADDING TO GRID
dataGridView1.Invoke(new Action(() => { dataGridView1.Rows.Add(new object[] { rows_nr, time_stamp, av[0], st[0], (t[0]).ToString(), (r[0]).ToString(), (t[1]).ToString(), (r[1]).ToString(), (t[2]).ToString(), (r[2]).ToString() }); }));
//dataGridView1.Rows.Add(new object[] { rows_nr, time_stamp, av[0], st[0], (t[0]).ToString(), (r[0]).ToString(), (t[1]).ToString(), (r[1]).ToString(), (t[2]).ToString(), (r[2]).ToString() });
dataGridView1.Invoke(new Action(() => { dataGridView1.FirstDisplayedScrollingRowIndex = dataGridView1.RowCount - 1; }));
//dataGridView1.FirstDisplayedScrollingRowIndex = dataGridView1.RowCount - 1;
// ADDING TO CHART
for (int i = 0; i < 3; i++)
chart1.Invoke(new Action(() => { chart1.Series[series_names[i]].Points.AddXY((rows_nr), (t[i])); }));
//chart1.Series[series_names[i]].Points.AddXY((rows_nr), (t[i]));
chart1.Invoke(new Action(() => { chart1.Series["average"].Points.AddXY((rows_nr), (av[0])); }));
//chart1.Series["average"].Points.AddXY((rows_nr), (av[0]));
//chart1.Series["std1"].Points.AddXY((rows_nr), (av[0] + Math.Abs(st[0])));
//chart1.Series["std2"].Points.AddXY((rows_nr), (av[0] - Math.Abs(st[0])));
// MOVING CHART
if (chart1.Series[series_names[0]].Points.Count > nr_of_noints_graph)
{
for (int i = 0; i < 3; i++)
chart1.Series[series_names[i]].Points.RemoveAt(0);
chart1.Series["average"].Points.RemoveAt(0);
//chart1.Series["std1"].Points.RemoveAt(0);
//chart1.Series["std2"].Points.RemoveAt(0);
chart1.ChartAreas[0].AxisX.Minimum = rows_nr - (nr_of_noints_graph - 1);
chart1.ChartAreas[0].AxisX.Maximum = rows_nr;
dataGridView1.Rows.RemoveAt(0);
}
chart1.Invoke(new Action(() => { chart1.ChartAreas[0].RecalculateAxesScale(); }));
//chart1.ChartAreas[0].RecalculateAxesScale();
}
Upvotes: 0
Views: 146
Reputation: 117019
When working with a background thread you must not create, update, or even access any UI element.
You need to separate the work that retrieves your data (the slow part) from the work that updates the chart (which is very fast).
It really comes down to doing it like this:
private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
if (!GetConnectionStatus())
{
stop_ticking();
return;
}
// TEMP READ
Read_temp(tlist);
float[] t = new float[3];
float[] r = new float[3];
float[] av = new float[1];
float[] st = new float[1];
// TEMP IMPORT
tlist.Give_current_temp(t, r, av, st);
string time_stamp = tlist.Give_current_time();
rows_nr++;
chart1.Invoke(new Action(() =>
{
// ADDING TO GRID
dataGridView1.Rows.Add(new object[] { rows_nr, time_stamp, av[0], st[0], (t[0]).ToString(), (r[0]).ToString(), (t[1]).ToString(), (r[1]).ToString(), (t[2]).ToString(), (r[2]).ToString() });
dataGridView1.FirstDisplayedScrollingRowIndex = dataGridView1.RowCount - 1;
// ADDING TO CHART
for (int i = 0; i < 3; i++)
{
chart1.Series[series_names[i]].Points.AddXY((rows_nr), (t[i]));
}
chart1.Series["average"].Points.AddXY((rows_nr), (av[0]));
// MOVING CHART
if (chart1.Series[series_names[0]].Points.Count > nr_of_noints_graph)
{
for (int i = 0; i < 3; i++)
{
chart1.Series[series_names[i]].Points.RemoveAt(0);
}
chart1.Series["average"].Points.RemoveAt(0);
chart1.ChartAreas[0].AxisX.Minimum = rows_nr - (nr_of_noints_graph - 1);
chart1.ChartAreas[0].AxisX.Maximum = rows_nr;
dataGridView1.Rows.RemoveAt(0);
}
chart1.ChartAreas[0].RecalculateAxesScale();
}));
}
If you have to show a MessageBox then you also need to invoke that.
Upvotes: 0
Reputation: 6427
As @Access Denied states you should improve separation between GUI and Background worker threads. You could execute // TEMP READ
and // TEMP IMPORT
operations on background thread and make a call to the GUI thread via .Invoke
method when all the data is ready. Read "How to: Make Thread-Safe Calls to Windows Forms Controls" article for more information.
When you add/update data in your DataGridView
use .BeginUpdate
/.EndUpdate
methods to prevent control update until all the data is refreshed.
Other approach is to use Virtual mode. It's especially usefull if you have many items in grid.
Upvotes: 0
Reputation: 9461
Please take a look at background worker sample. You are doing it wrong. Background worker DoWork should not call UI controls and is executed in non UI thread, it should execute time consuming computing and call worker.ReportProgress(). While ReportProgress method can access UI controls and code in this method is executed in UI thread. Some chart controls are lugging when adding/removing points. Maybe it hangs because it lugs. Make updates less frequently (1 in 1 second for example) and see whether it hangs or not.
Wrap operations in Stopwatch and use System.Diagnostics.Debug.WriteLine to trace execution flow and time spent on the operations.
Moving chart part does not work because it accesses UI elements in non ui thread without Invoke to UI thread.
If it was not Background worker I would write it this way:
// MOVING CHART
chart1.Invoke(new Action(()=>
{
if (chart1.Series[series_names[0]].Points.Count > nr_of_noints_graph)
{
for (int i = 0; i < 3; i++)
chart1.Series[series_names[i]].Points.RemoveAt(0);
chart1.Series["average"].Points.RemoveAt(0);
chart1.ChartAreas[0].AxisX.Minimum = rows_nr - (nr_of_noints_graph - 1);
chart1.ChartAreas[0].AxisX.Maximum = rows_nr;
}
}
));
I wouldn't wrap each operation in separate Invokes as well.
As for your question it's insufficient information to detect what is wrong please provide minimum viable runnable sample which demonstrates the problem.
Upvotes: 1