The task of translating drawing coordinates into DataPoints and back is not exactly intuitive.
It is possible but you need to know the rules and pay a certain price.
I have outlined the way in this post and it is worth looking into..
But as the problem is coming up repeatedly, here is a more general solution.
Here is how to call it:
private void button11_Click(object sender, EventArgs e)
{
valuePoints.Add(new PointF(640, 1));
valuePoints.Add(new PointF(670, 10));
paintToCalaculate = true;
chart1.Invalidate();
}
And here is the result: Two red points drawn at the values 640, 1
and 670, 10
:
The points are placed correctly event though I have zoomed and scrolled in the chart and also resized it..
To make it work we need three class level variables: A flag and two point lists. One list is the input with the values in the chart where the points are, the other is the output, receiving the current pixel coordinates.
bool paintToCalaculate = false;
List<Point> drawPoints = new List<Point>();
List<PointF> valuePoints = new List<PointF>();
I use a flag to avoid recalculating the Points when the system causes redraws. And after setting it I trigger the Paint
event by Invalidating
the Chart
. During the Paint
event I reset the flag.
Please note that these values are very volatile: They change:
- Whenever you zoom or scroll
- Whenever you resize the chart
- Whenever the layout changes, maybe because new points need room or trigger a change in a label format..
Therefore those drawing coordinates will have to be updated on each such event!
Here is one example, that takes care of zoom and scrolling:
private void chart1_AxisViewChanged(object sender, ViewEventArgs e)
{
paintToCalaculate = true;
chart1.Invalidate();
}
You need to add these two lines, or a function to wrap them, to a few other spots in your program, depending what things you allow to happen in the chart.. Resize
is also an obvious candidate..
Now for the actual caculation. It is using the ValueToPixelPosition
, which does all the work. Unfortunately is only works inside of any of the three paint events of a chart (PrePaint
,Paint
and PostPaint
) . Here I use the normal Paint
event.
private void chart1_Paint(object sender, PaintEventArgs e)
{
if (paintToCalaculate)
{
Series s = chart1.Series.FindByName("dummy");
if (s == null) s = chart1.Series.Add("dummy");
drawPoints.Clear();
s.Points.Clear();
foreach (PointF p in valuePoints)
{
s.Points.AddXY(p.X, p.Y);
DataPoint pt = s.Points[0];
double x = chart1.ChartAreas[0].AxisX.ValueToPixelPosition(pt.XValue);
double y = chart1.ChartAreas[0].AxisY.ValueToPixelPosition(pt.YValues[0]);
drawPoints.Add(new Point((int)x, (int)y));
s.Points.Clear();
}
paintToCalaculate = false;
chart1.Series.Remove(s);
}
//..
// now we can draw our points at the current positions:
foreach (Point p in drawPoints)
e.Graphics.FillEllipse(Brushes.Red, p.X - 2, p.Y - 2, 4, 4);
}
Note that I add and remove a dummy Series
and add and clear one Point
to it for each data point, just to calculate its pixel coordinates. Yes, a bit involved, but the results are worth it..
I assume you can change the Button_Click
code to read in the values from your TextBoxes
instead of the using my hard-coded numbers..