Search code examples
c#selectchartsmschart

Selecting specific values on a Chart


I'm trying to create a sort of copy and paste function with the data on my graph and I was wondering if there was any way to get the x position of a point on the chart when it is clicked?

Basically, the idea is to be able to click a portion of the graph and drag to select an area, which I will then process accordingly.

So, I need to be able to figure out where on the graph the user has clicked to determine the what the first point of the selected area will be.

I looked through the chart API, but I couldn't seem to find anything useful for this type of problem..


Solution

  • For directly clicking on a DataPoint you can do a HitTest. But for tiny points or for a selection of a range this will not work well.

    The necessary functions are hidden in the Axes methods.

    This solution uses a regular rubber-band rectangle to select the points caught:

    enter image description here

    Point mdown = Point.Empty;
    List<DataPoint> selectedPoints = null;
    
    private void chart1_MouseDown(object sender, MouseEventArgs e)
    {
        mdown = e.Location;
        selectedPoints = new List<DataPoint>();
    }
    
    private void chart1_MouseMove(object sender, MouseEventArgs e)
    {
        if (e.Button == System.Windows.Forms.MouseButtons.Left)
        {
            chart1.Refresh();
            using (Graphics g = chart1.CreateGraphics())
                g.DrawRectangle(Pens.Red, GetRectangle(mdown, e.Location));
        }
    }
    
    private void chart1_MouseUp(object sender, MouseEventArgs e)
    {
        Axis ax = chart1.ChartAreas[0].AxisX;
        Axis ay = chart1.ChartAreas[0].AxisY;
        Rectangle rect = GetRectangle(mdown, e.Location);
    
        foreach (DataPoint dp in chart1.Series[0].Points)
        {
            int x = (int)ax.ValueToPixelPosition(dp.XValue);
            int y = (int)ay.ValueToPixelPosition(dp.YValues[0]);
            if (rect.Contains(new Point(x,y))) selectedPoints.Add(dp);
        }
    
        // optionally color the found datapoints:
        foreach (DataPoint dp in chart1.Series[0].Points)
            dp.Color = selectedPoints.Contains(dp) ? Color.Red : Color.Black;
    }
    
    static public Rectangle GetRectangle(Point p1, Point p2)
    {
        return new Rectangle(Math.Min(p1.X, p2.X), Math.Min(p1.Y, p2.Y),
            Math.Abs(p1.X - p2.X), Math.Abs(p1.Y - p2.Y));
    }
    

    Note that this will work for Line, FastLine and Point charts. For other types you would have to adapt the selection criterium!