0

So far, my C# program has been taking real-time input from an COM gate, and draw it on a chart, like so:

// DATA is the input from the COM gate, TIMESTAMP is the time the data is taken  
DataChart.Series["Data"].Points.AddXY(TIMESTAMP, DATA);
// Continue processing

I did not save the data into a separate array or class because they are rather large, and have caused our website namesake before.
Now the program is needed to "cut" the chart between two time points (called StartTime and EndTime) into another chart (called CutChart). Since I still do not want to keep them long term, I tried this:

foreach (Series series in DataChart.Series)
    foreach(DataPoint point in series.Points)
        if (point.XValue <= EndTime && point.XValue >= StartTime) 
            CutChart.Series[DataChart.Series.IndexOf(series)].Points.Add(point);

Yet it does not work (As in, the CutChart, which start with no point, and therefore appear empty, once that code line run, still appear empty, no error or exception recorded).
Strangely enough, when I add points in DataChart wrongly (TIMESTAMP in Y axis instead of X), the line of code above work perfectly.
The way I understand it, to draw a graph, the C# Chart class must be saving the XY coordinate of each point... somewhere. To produce a new graph that is a portion of the old graph between two points in time, just add the points with satisfactory X-value to the new graph. Except I do not know where that "somewhere" is.
Note: I need to draw the "cut" part of DataChart on a new chart, so just zoom in is not quite enough.

  • _Yet it does not work._ That is not a helpful problem description! - Of course you know that any setting of axes properties must be copied or modified as well..? _must be saving the XY coordinate of each point... somewhere._ No, the pixel values are re-calculated whenever the chart shows. And the data values are, of course, part of the DataPoint properties. In case you actually wanted to know the pixel values you could use the axes function to calculate them from the data- or the position-values, btw, but your code looks fine to me.. – TaW Aug 02 '17 at 04:21
  • I cannot describe it any more precisely than "does not work" because it went through that code line, no error, no exception, just a whole lot of nothing. When people add points into a chart series, the format is "AddXY(X, Y)", or value X, Y => a point in chart, which means logic would dictate that there is a way to "reverse" that code line, essentially a point in chart => value X, Y – Nam Nguyen Hoang Aug 02 '17 at 05:01
  • Well, what happens????? __Is the Chart empty?__ Does it have a ChartArea? ([You need to add one if you have created the Chart](https://stackoverflow.com/questions/36726650/dynamically-creating-charts/36728360?s=3|0.3843#36728360), btw!!) Can you see the axes?? – TaW Aug 02 '17 at 06:33
  • I cannot see the axes of the CutChart, the DataChart is not empty, the Cutchart already have a ChartArea[0] though – Nam Nguyen Hoang Aug 02 '17 at 07:21
  • I meant is the CutChart empty? But it isn't once you can see the axes. You never confimred that you made sure any axes properties you may have set are reproduced with sensible values. Of course the next most simple explanation is that the Start/EndTime values are wrong.. Did you use DateTime types? Did you use the double conversion?? (`ToOADate`) - Next thing to do is use the debugger and look into the Points collection of a Series. – TaW Aug 02 '17 at 07:25
  • That's it! The CutChart is empty, without any axes showing due to the axes maximum / minimum are set wrong. I have fixed the program now. If you can combine all your comments (about setting ChartArea, check axes, etc.) and work it into an answer (sort of like a checklist for C# chart), I will accept it. Always the smallest thing is the biggest spanner in the work! – Nam Nguyen Hoang Aug 02 '17 at 07:47

2 Answers2

0

I've tested your code successfully so the problem is likely somewhere else. I think you would make your life easier by using the Datasource item instead of having to manipulate points and their coordinates yourself.

public partial class Form1 : Form
{
    public Form1()
    {
        InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        chart1.Series.Add("test");
        chart1.Series[0].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
        chart1.Series[0].Points.AddXY(1, 1);
        chart1.Series[0].Points.AddXY(2, 2);
        chart1.Series[0].Points.AddXY(3, 3);


        chart2.Series.Add("test2");
        chart2.Series[0].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
    }

    private void button1_Click(object sender, EventArgs e)
    {
        foreach (Series series in chart1.Series)
            foreach (DataPoint point in series.Points)
                if (point.XValue >= 2)
                    chart2.Series[chart1.Series.IndexOf(series)].Points.Add(point);
    }
}

Edit: What I mean by using datasource is make use of the binding capability of the Chart to decouple your data from its representation, in short, to not have to copy all data points from one chart to another every time.

for example:

        //this is just dummy data, see https://msdn.microsoft.com/en-us/library/system.windows.forms.datavisualization.charting.chart.datasource(v=vs.110).aspx
        List<Tuple<int, int>> data = new List<Tuple<int, int>>() { new Tuple<int, int>(1,1), new Tuple<int, int>(2,5) }; 

        //this code bind your data source to your chart
        chart1.DataSource = data;
        chart1.Series.Add("test");
        chart1.Series[0].ChartType = System.Windows.Forms.DataVisualization.Charting.SeriesChartType.Line;
        chart1.Series[0].XValueMember = "item1";
        chart1.Series[0].YValueMembers = "item2";
        chart1.DataBind();

        // now to create your "cut" chart you can simply query the data you want from the data source and use the same code as above to create another chart.
        var subdata = data.Where(x => x.Item1 > 1);
Etienne
  • 1,058
  • 11
  • 22
  • Thank you for answering! I am clearing up as much of the program as possible to identify the problem. Can you be more precise on what you mean by "Datasource item"? I don't have a formal education on programming. – Nam Nguyen Hoang Aug 02 '17 at 05:13
  • The 1st sentence is correct, the rest makes no sense imo. – TaW Aug 02 '17 at 06:42
  • @TaW, do you mean the answer or my comment? – Nam Nguyen Hoang Aug 02 '17 at 07:15
  • Sorry, I meant the answer. Even though he now shows DataBinding, it has no extra value in this case. - So: How did you create the Chart? From the Toolbox or in code?? – TaW Aug 02 '17 at 07:18
  • @Etienne That is a good suggestion, though I do not want to store the data multiple time, since the process between each data input is as resource intensive as it is ("our website namesake" is [Stack Overflow] (https://stackoverflow.com/questions/206820/how-do-i-prevent-and-or-handle-a-stackoverflowexception)) – Nam Nguyen Hoang Aug 02 '17 at 07:56
0

You wrote

Strangely enough, when I add points in DataChart wrongly (TIMESTAMP in Y axis instead of X), the line of code above work perfectly.

This may be a hint that the problem is with the data types.

Since the x-values are entered from DateTime variables one might conclude that one can compare them to DateTime variables.

But internally all values, x- and y-values are stored as doubles.

So you need to convert any date you want to use in a comparison to double:

var datetime1 = DateTime.Now;
var dDouble = datetime1.ToOADate();

There is also a conversion from the double values:

var datetime2 = DateTime.FromOADate(dDouble );

or

var datetime3 = DateTime.FromOADate(aDataPoint.XValue);

So your condition may be written as

if (point.XValue <= EndTime.ToOADate() && point.XValue >= StartTime.ToOADate()) 

But it is cleaner to do the conversion only once, of course..:

double dStarttime = StartTime.ToOADate();
double dEndTime = EndTime.ToOADate();
..
..
if (point.XValue <= dEndTime  && point.XValue >= dStarttime ) 

Other reasons why a Chart may appear empty are:

  • No ChartArea. If you create the chart in code, as oposed to dropping it from the toolbox, it will not have a ChartArea.
  • No Points. No matter how nicely you style the axes, there still must be at least one dummy point before anything shows up.
TaW
  • 53,122
  • 8
  • 69
  • 111
  • I don't have much experience using the C# chart class so I need to ask you this, out of curiosity, why would one prefer doing conversion from-to chart coordinate rather than using the databinding system that handles all of that for you ? I've used web chart more (kendo UI) and it would never occur to me to manipulate the coordinates of the data manually. It's fastidious, error prone, hard to test, and it's already been done for you... – Etienne Aug 03 '17 at 00:24
  • _conversion from-to chart coordinate rather than using the databinding_ hm, not sure what you mean here. Are you talking about the OADate functions or about the AddXY method or something else altogether? – TaW Aug 03 '17 at 01:40
  • what happens if you change the units of your axis ? Don't you need to update manually all your datapoints ? – Etienne Aug 03 '17 at 01:49
  • Um, not as long as the base type stays DateTime. - Whether one uses DataBinding is a matter of choice. One can do it either wqy. To use a Filter with data binding you would insert a BindingSource and apply a Filter to it. Since it would refer to the fields of the DataSource it would not need to use the ToOadate conversion.. – TaW Aug 03 '17 at 05:16