How to display nearest data point information of a line chart by moving the mouse over the chart area

Viewed 1249

I'm going to draw a chart using c# in Windows Form Application. I need to draw a circle on line chart and show this data point value on a label which is nearest data point from the x-axis of the mouse pointer when mouse is moved over the chart area.

I write a code as follows......

private void Chart1_MouseMove(object sender, MouseEventArgs e)
    {
        HitTestResult result = Chart1.HitTest(e.X, e.Y);

        DataPoint nearestPoint = null;

        if (prevPosition!=null)
        {

            Chart1.Series[0].Points[prevPosition.PointIndex].MarkerStyle = MarkerStyle.None;


        }


        if (result.ChartElementType == ChartElementType.DataPoint)
        {


            string xValue = DateTime.FromOADate(Chart1.Series[0].Points[result.PointIndex].XValue).ToString("yyyy/MM/dd");
            string yValue = Convert.ToString(Chart1.Series[0].Points[result.PointIndex].YValues[0]);

            Chart1.Series[0].Points[result.PointIndex].MarkerStyle = MarkerStyle.Circle;
            Chart1.Series[0].Points[result.PointIndex].MarkerSize = 7;
            Chart1.Series[0].Points[result.PointIndex].MarkerColor = Color.Green;

            label1.Text = "Date:" + xValue;
            label2.Text = "Price:" + yValue;
            prevPosition = result;

        }

But this code shows the value and corresponding circle over line when mouse is moved near to the depicted line. When mouse is far away from line but within chart area it does not show the circle and value. I need to draw the circle over the line point nearest to the X-axis of the mouse pointer and show this data on a label

1 Answers

You can find the closest point measuring only the x-values or the y-values or measuring the absolute distances. Or you could simple output the values under the mouse cursor, no matter the points. For this last one see here!

For each of the 1st three options this should help:

Class level variables used to set and reset colors..:

DataPoint dpXaxis = null;
DataPoint dpYaxis = null;
DataPoint dpAbs = null;

And a list of points for keeping the pixel locations of all points:

List<Point> pixPoints = null;

The MouseMove event:

private void chart_MouseMove(object sender, MouseEventArgs e)
{
    ChartArea ca = chart.ChartAreas[0];
    Axis ax = ca.AxisX;
    Axis ay = ca.AxisY;
    Series s = chart.Series[0];

    if (!s.Points.Any()) return; // no data, no action!

    // option 1:
    // the values at the mouse pointer:
    double valx = ax.PixelPositionToValue(e.X);
    double valy = ay.PixelPositionToValue(e.Y);

    // the deltas on the x-axis (with index):
    var ix = s.Points.Select((x, i) => new {delta = Math.Abs(x.XValue - valx), i})
                            .OrderBy(x => x.delta).First().i;
    var dpx = s.Points[ix];

    // option 2:
    // the deltas on the y-axis (with index):
    var iy = s.Points.Select((x, i) => 
                      new {delta = Math.Abs(x.YValues[0] - valy), i })
                     .OrderBy(x => x.delta).First().i;
    var dpy = s.Points[iy];

    // option 3:
    // the absolute distances (with index):
    var ind = pixPoints.Select((x, i) =>
        new { delta = Math.Abs(x.X - e.X) + Math.Abs(x.Y - e.Y), i}).
        OrderBy(x => x.delta).First().i;
    // find index of smallest delta
    var dpca = s.Points[ind];


    // set/reset colors
    if (dpXaxis != null) dpXaxis.Color = s.Color;
    dpXaxis = dpx;
    dpXaxis.Color = Color.LawnGreen;

    // set/reset colors
    if (dpYaxis != null) dpYaxis.Color = s.Color;
    dpYaxis = dpy;
    dpYaxis.Color = Color.Cyan;

    if (dpAbs != null) dpAbs.Color = s.Color;
    dpAbs = dpca;
    dpAbs.Color = Color.Red;
}

To find the point closest in both directions you will need to either include the scales of the axes or, probably easier, create a List<PointF> from the DataPoints that hold the locations of the points in pixels. For this use the reverse axis functions. Then I calculate the deltas in a similar fashion as the Linq above.

The list gets filled/updated like so:

List<Point> getPixPoints(Series s, ChartArea ca)
{
    List<Point> points = new List<Point>();
    foreach (DataPoint dp in s.Points)
    {
        points.Add(new Point(
            (int)ca.AxisX.ValueToPixelPosition(dp.XValue),
            (int)ca.AxisY.ValueToPixelPosition(dp.YValues[0])  ));
    }
    return points;
}

Let's see it at work:

enter image description here

Related