Search code examples
c#.netchartslabelmschart

Double label while adding a datapoint in mschart


I'm writing a code in C#.Net WinForms to add label on clicking some datapoints on chart. Before clicking the chart, the graph looks like this.

Chart before click

Now I click a datapoint between 330-340. The chart shows selected datapoint with double label (338.61). As shown below: Chart after click

Below is the debug output: Debug Output

Here is the below code for chart_mouseclick

private void chart1_MouseClick(object sender, MouseEventArgs e)
{
    if (e.Button == MouseButtons.Left)
    {               
        double mouse_Xvalue = chart1.ChartAreas[0].AxisX.PixelPositionToValue(e.X);
        double mouse_Yvalue = chart1.ChartAreas[0].AxisY.PixelPositionToValue(e.Y);

        DataPoint Prev_DataPoint = chart1.Series[0].Points.Select(x => x)
            .Where(x => x.XValue >= mouse_Xvalue)
            .DefaultIfEmpty(chart1.Series[0].Points.First()).First();

        DataPoint Next_DataPoint = chart1.Series[0].Points.Select(x => x)
            .Where(x => x.XValue <= mouse_Xvalue)
            .DefaultIfEmpty(chart1.Series[0].Points.Last()).Last();

        DataPoint Add_DataPoint = Math.Abs(Prev_DataPoint.XValue - mouse_Xvalue) < Math.Abs(Next_DataPoint.XValue - mouse_Xvalue) ? Prev_DataPoint : Next_DataPoint;

        int add_data_point_index = chart1.Series[0].Points.IndexOf(Add_DataPoint);

        DataPoint max = Add_DataPoint;
        for (int i = add_data_point_index - 10; i <= add_data_point_index + 10; i++)
        {
            DataPoint dp = chart1.Series[0].Points[i];

            if (dp.YValues[0] > max.YValues[0])
            {
                add_data_point_index = i;
                max = dp;
            }
        }

        chart1.Series[1].Points.Add(max);
        chart1.Series[1].Sort(PointSortOrder.Ascending, "X");

        for (int i = 0; i < chart1.Series[1].Points.Count; i++)
        {
            chart1.Series[1].Points[i].Label = 
            Math.Round(chart1.Series[1].Points[i].XValue, 2).ToString();
        }                 
    }
}

What is wrong with the code ?


Solution

  • This is actually a quite interesting observation..

    Let's look at the second version, which works fine for you:

    chart1.Series[1].Points.AddXY(max.XValue, max.YValue[0])
    

    or in short:

    series1.Points.AddXY(x,y);
    

    This is the normal way to add DataPoints: A new point is created with the two (or more) values and all other properties taken from the Series defaults.

    The 'other' properties include colors, markers and label data, including IsValueShownAsLabel .

    No surprises here.

    Now for the original version:

    chart1.Series[1].Points.Add(max);
    

    or in short

    series1.Points.Add(dp);
    

    where max (or dp) are DataPoints in the first series (series0).

    This behaves surprisingly different..:

    One could imagine that the DataPoint gets moved (like Controls would be) but it isn't. Or cloned like strings would be.

    Instead only a new reference is created and added to series1.Points. This has several surprising consequences..:

    • Both references point to the same datapoint object; so suddenly there is a point in series1 with properties taken from series0, including IsValueShownAsLabel!

    • As both series now contain a datapoint with the same values/coordinates the chart is smart enough to display their labels slightly apart to avoid overlapping. This is the effect you see.

    • If you now remove the datapoint from its original series0, series1 will still contain a reference to it with color, label etc as it was in series0..

    So there indeed is no way to make the original version work since two references to the same point will always make the chart display the label either twice or not at all.