Page 1 of 1

Using a DataTable as DataSource

Posted: Tue Sep 15, 2015 9:48 am
by 15673859
I had troubles with using a DataTable as DataSource in a multithreading application and TeeChart.net 4.1.2015.08060.
The structure:
A thread collects data from a external device and add them into a DataTable. Cycle: < 1 Second.
For each column (except 1st column containing a TimeStamp) a "line" series is created. The DataSource of the series is set to the DataTable.

Code: Select all

foreach (DataColumn col in dataTable.Columns) 
{
   if (col == dataTable.Columns[0]) continue; 
   Steema.TeeChart.Styles.Line lineSeries = new Steema.TeeChart.Styles.Line(); 
   ChartControl.Series.Add(lineSeries);
   lineSeries.DataSource = dataTable; //set DataTable as DataSource
   lineSeries.YValues.DataMember = col.ToString();
   lineSeries.XValues.DataMember = timeCol.ToString();
   lineSeries.XValues.DateTime = true; 
   lineSeries.Title = col.ToString();
   lineSeries.Active = false; 
...
}
The collector thread fires an event, if the DataTable is updated.
The EventHandler for updating the Teechart look like this:

Code: Select all

for (int i = 0; i < ChartControl.Series.Count; i++)
{
    if (ChartControl[i].Active && Drawing == false)
         ChartControl[i].CheckDataSource();
}
ChartControl.Invalidate(); 
Drawing = true if ChartControl.BeforeDraw is fired.
Drawing = false if ChartControl.AfterDraw is fired.

Before I introduce the "Drawing" property, I always get troubles with displaying the Series and at the end there was the "red cross".
Now it is working fine, but there are many open questions.

Do I understand right, that I only have to ensure, that the CheckUpdateSource is not called during TeeChart is drawing.
Is there a more elegant way to avoid "red cross" in my situation?
What do Series.BeginUpdate() and Series.EndUpdate() doing? Could you explain what "Recalculates the function just one time, when finished adding points." means!

I attached the "stress test" project, I used to find a solution for my DataTable problem.

Thank you for your comments...

Re: Using a DataTable as DataSource

Posted: Tue Sep 15, 2015 4:24 pm
by Christopher
Hello,
zhao wrote: Is there a more elegant way to avoid "red cross" in my situation?
Unfortunately GDI+ does not support multithreading, as we discovered some time ago (see here). This means that TeeChart does not support multithreading - data cannot be entered into it by more than one thread.

My best suggesting is to put the code that runs TeeChart in a critical section using the lock statement.

Re: Using a DataTable as DataSource

Posted: Tue Sep 15, 2015 9:27 pm
by 15673859
Hello Christopher,

Tanks for your answer!
If you take a look in my attached application, you will see, there is only one thread, which is putting data into the DataTable. This DataTable is bound to the teechart in the GUI-Thread.
A event signals, that theechart should update, if it is not busy and drawing at the moment. If TheeChart is not ready, it trys next update again.

Why and what should I put in a critical section?
Christopher: "The code that runs Teechart"
I'm sorry! Could you give me a hint, maybe related to my example application, please?
Do I also have to protect the write access to the DataTable (dataTable.Rows.Add()), if TeeChart is drawing and reading the DataTable).

Is my usage of "BeforeDraw" and "AfterDraw" correct?
Would be Series.BeginUpdate() and Series.EndUpdate() helpful? Could you give me a short explanation?

Could you describe, what exactly causes the "red cross error".

Best

Zhao

Re: Using a DataTable as DataSource

Posted: Wed Sep 16, 2015 9:07 am
by Christopher
Hello,
zhao wrote:Could you describe, what exactly causes the "red cross error".
Apologies for not answering your questions in the detail that maybe you would like. Before I do so, could I please ask you which version of TeeChart.dll you are using? Using the latest version, this is the result of running your code:

http://screencast.com/t/3tzE5x82KV4

note that I have only made one change to your example project, which was to set:

Code: Select all

ChartControl.Zoom.Active = false;
You may like to download the evaluation version of the latest version from here to see if your code works as expected with it.

Re: Using a DataTable as DataSource

Posted: Wed Sep 16, 2015 9:11 pm
by 15673859
Version is 4.1.2015.8064 (Net 4.51)

Your video shows, what I expect.
Thank you for turning off zoom!
If you switch off "Check TeeChart drawing" (lower left corner) you get the red cross error after a few seconds.
Presumably because "CheckDataSource()" is called, even though the last drawing was not finished yet.
But "Check TeeChart drawing" is not enough, right?

Zhao

Re: Using a DataTable as DataSource

Posted: Wed Sep 16, 2015 9:43 pm
by Christopher
Zhao,
zhao wrote:Version is 4.1.2015.8064 (Net 4.51)
If you switch off "Check TeeChart drawing" (lower left corner) you get the red cross error after a few seconds.
Okay, I'm using the same version here and have unchecked "Check TeeChart drawing" - the app has been running for three minutes now and no red cross.

I'm running this on Windows 10 - which OS are you using?

Re: Using a DataTable as DataSource

Posted: Thu Sep 17, 2015 8:19 pm
by 15673859
My OS is Windows 8.1

Re: Using a DataTable as DataSource

Posted: Thu Sep 17, 2015 10:40 pm
by 15673859
The time to red cross depends on hardware and mainwindow size.

Here you can follow 1 minute of runtime on my comuter. I check and uncheck "Check Teechart Drawing" several times
http://www.screencast.com/t/p0QVqKqq

Re: Using a DataTable as DataSource

Posted: Fri Sep 18, 2015 1:17 pm
by Christopher
zhao wrote:Here you can follow 1 minute of runtime on my comuter. I check and uncheck "Check Teechart Drawing" several times
Okay, thank you. Unfortunately I still can't reproduce the issue here, but I do see that it is a threading error. It is always difficult to suggest fixes without having empirically tested them first, but what happens if you try this?:

Code: Select all

    private Object thisLock = new Object();

    private void OnHistoryUpdated(object sender, CollectorHistoryUpdatedEventArgs e)
    {
      lock (thisLock)
      {
        if (this.dataTabledirect.Checked)
        {
          if (this.checkDrawing.Checked == false)
            Drawing = false;
          for (int i = 0; i < ChartControl.Series.Count; i++)
          {
            if (ChartControl[i].Active && Drawing == false)
              ChartControl[i].CheckDataSource();
          }
        }
        else
        {
          try
          {
            for (int i = 0; i < ChartControl.Series.Count; i++)
            {
              DataRow dRow = e.Row;
              DateTime dt = (DateTime)dRow[0];
              double val = (double)dRow[i + 1];
              ChartControl[i].Add(dt, val);
            }
          }
          catch (Exception ex)
          {
            MessageBox.Show(ex.Message, "OnHistoryUpdated Exception");
          }

        }
        if (this.invalidateChart.Checked)
          ChartControl.Invalidate(); //necessary, that it keeps updating
      }
    }