从文本框中的图表值获取绘图点的坐标?

Get coordinates of a drawing point from Chart Values in TextBoxes?

在设计器中我有两个文本框。 还有一个图表控件。 我希望当我在第一个文本框中键入数字 120 并在第二个文本框中键入数字 1 时,它将在 120,1 的图表上绘制一个点,但我的意思是 120 和 1 作为 x 轴和 y 轴值。

红色实心圆圈不在 120 和 1 处。 我的意思是红色圆圈应该画在 120 的左轴上。 如果我将 120 116 而不是 1 25,那么应该在左轴 116 和底轴 25 上绘制圆。

但是现在圆是从图表中画出来的

这是我的代码:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Windows.Forms.DataVisualization.Charting;
using System.Drawing.Drawing2D;
using System.Collections;

namespace Test
{
    public partial class Form1 : Form
    {
        private Point startPoint = new Point();
        private Point endPoint = new Point();
        private int X = 0;
        private int Y = 0;
        private List<Point> points = new List<Point>();
        private Point lastPoint = Point.Empty;
        private ArrayList myPts = new ArrayList();

        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            Random rdn = new Random();
            for (int i = 120; i > 0; i--)
            {
                chart1.Series["Series1"].Points.AddXY
                    (rdn.Next(0, 10), rdn.Next(0, 10));
            }

            chart1.Series["Series1"].ChartType = SeriesChartType.FastLine;
            chart1.Series["Series1"].Color = Color.Red;

            ChartArea area = chart1.ChartAreas[0];

            area.AxisX.Minimum = 1;
            area.AxisX.Maximum = 30;
            area.AxisY.Minimum = 1;
            area.AxisY.Maximum = 120;

            LineAnnotation line = new LineAnnotation();
            Point p1 = new Point(1, 120);
            chart1.Annotations.Add(line);
            line.AxisX = area.AxisX;
            line.AxisY = area.AxisY;
            line.IsSizeAlwaysRelative = false;
            line.X = 1; line.Y = 120;
            line.Right = 30; line.Bottom = 1;
            line.LineColor = Color.Blue;
            line.LineWidth = 3;
        }

        SolidBrush myBrush = new SolidBrush(Color.Red);
        private void chart1_Paint(object sender, PaintEventArgs e)
        {            
            Graphics g = e.Graphics;
            foreach (Point p in myPts)
            g.FillEllipse(myBrush, p.X, p.Y, 10, 10);            
        }

        private void chart1_MouseClick(object sender, MouseEventArgs e)
        {
            myPts.Add(new Point(X,Y));
            chart1.Invalidate();
        }

        private void txtT_TextChanged(object sender, EventArgs e)
        {
            X = int.Parse(txtWeight.Text);
        }

        private void txtDays_TextChanged(object sender, EventArgs e)
        {
            Y = int.Parse(txtDays.Text);
        }
    }
}

我所做的是,在我输入两个文本框值之后,当我用鼠标单击图表控制区域的任意位置时,它应该在文本框的坐标上绘制圆圈。

但是圆没有画在正确的地方。

文本框名称txtT 是左边轴上左边的值。 textBox txtDays 应该是底部值的轴。

将绘图坐标转换为数据点并返回的任务并不十分直观。

可以,但需要了解规则,付出一定的代价。

我已经概述了方法,值得研究..

但由于问题反复出现,这里有一个更通用的解决方案。

调用方法如下:

private void button11_Click(object sender, EventArgs e)
{
    valuePoints.Add(new PointF(640, 1));
    valuePoints.Add(new PointF(670, 10));
    paintToCalaculate = true;
    chart1.Invalidate();
}

结果如下:在值 640, 1670, 10 处绘制的两个红点:

尽管我在图表中进行了缩放和滚动并调整了大小,但这些点的位置是正确的..

为了让它工作,我们需要三个 class 级别变量:一个标志和两个点列表。一个列表是图表中点所在的值的输入,另一个是输出,接收当前像素坐标。

bool paintToCalaculate = false;
List<Point> drawPoints = new List<Point>();
List<PointF> valuePoints = new List<PointF>();

我使用了一个标志来避免在系统导致重绘时重新计算点数。设置后,我通过 InvalidatingChart 触发了 Paint 事件。在 Paint 事件期间,我重置了标志。

请注意这些值非常易变:它们变化:

  • 每当您缩放或滚动时
  • 每当您调整图表大小时
  • 每当布局发生变化时,可能是因为新点需要空间或触发标签格式的变化..

因此,这些绘图坐标必须在每个此类事件中更新

这是一个示例,它负责缩放和滚动:

private void chart1_AxisViewChanged(object sender, ViewEventArgs e)
{
    paintToCalaculate = true;
    chart1.Invalidate();
}

您需要将这两行或将它们包装起来的函数添加到程序中的其他几个位置,具体取决于图表中允许发生的事情。Resize 也是显而易见的候选人..

现在进行实际计算。它使用 ValueToPixelPosition 来完成所有工作。不幸的是,仅适用于图表的三个绘制事件(PrePaintPaintPostPaint)中的任何一个 inside。这里我使用正常的 Paint 事件。

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);
}

请注意,我为每个数据点添加和删除了一个虚拟 Series 并添加和清除了一个 Point,只是为了计算其像素坐标。是的,有点复杂,但结果是值得的..

我假设您可以更改 Button_Click 代码以从您的 TextBoxes 中读取值,而不是使用我的 hard-coded 数字..