C# WinForms Chart绘制点数量超出指定值问题排查
解决WinForm Chart绘制大量额外点的问题
嘿,终于找到你问题的根源啦!你看到的那些额外的线状点,根本不是什么图形归一化的锅,而是**chart1_Paint事件被无限触发**导致的——你在Paint方法里直接给Chart的Series添加点,这个操作会让Chart控件触发重绘,进而再次调用chart1_Paint,循环往复,点就越堆越多,看起来就像多了很多莫名其妙的点。
核心问题拆解
每次执行chart1.Series[0].Points.AddXY(),Chart控件会认为自己的数据发生了变化,需要重新渲染,于是自动触发Paint事件。而你的Paint方法里又在添加点,这就形成了无限循环,点的数量会指数级增长,最终出现你看到的异常效果。
修复方案:分离数据更新与绘制逻辑
正确的做法是:数据更新(添加Chart点)放到按钮点击事件里,Paint事件只负责绘制静态的连线(比如窗口重绘、大小变化时需要重新画连线)。
步骤1:修改按钮点击事件(数据更新逻辑)
把原来Paint里计算点坐标、添加Chart点的逻辑移到按钮点击事件里,同时清空旧点避免重复添加,最后保存数据到类成员变量供Paint使用:
// 先在类里定义两个成员变量,用来保存绘制所需的数据 private List<Point> _currentPointsToDraw; private IEnumerable<Tuple<int, int>> _currentTree; private void YourDrawButton_Click(object sender, EventArgs e) { // 先清空Series里的旧点,避免重复添加 chart1.Series[0].Points.Clear(); List<Point> pointsToDraw = new List<Point>(); List<int> pointsList = new List<int>(); int[] points; if (Tree != null) { // 收集所有节点并去重排序 foreach (Tuple<int, int> tp in Tree) { pointsList.Add(tp.Item1); pointsList.Add(tp.Item2); } IEnumerable<int> pointsDistinct = pointsList.Distinct().OrderBy(s => s); points = pointsDistinct.ToArray(); // 初始化点集合 for (int i = 0; i < points.Length; i++) { pointsToDraw.Add(new Point(begX, begY)); } int y = 1; List<int> pointsAdded = new List<int>(); for (int i = 0; i < points.Length; i++) { foreach (Tuple<int, int> tp in Tree) { if (points[i] == tp.Item1) { // 优化判断逻辑,用Contains替代Count() == 0 if (!pointsAdded.Contains(points[i])) { pointsToDraw[tp.Item1] = new Point(begX, begY); begX += 10; begY -= 15; pointsAdded.Add(points[i]); } if (!pointsAdded.Contains(tp.Item2)) { pointsToDraw[tp.Item2] = new Point(pointsToDraw[tp.Item1].X + (10 * y), pointsToDraw[tp.Item1].Y - 15); pointsAdded.Add(tp.Item2); } } if (points[i] == tp.Item2) { if (!pointsAdded.Contains(points[i])) { pointsToDraw[tp.Item2] = new Point(begX, begY); begX -= 10; begY -= 15; pointsAdded.Add(points[i]); } if (!pointsAdded.Contains(tp.Item1)) { // 原代码这里写错了,应该给tp.Item1赋值,不是tp.Item2 pointsToDraw[tp.Item1] = new Point(pointsToDraw[tp.Item1].X + (10 * y), pointsToDraw[tp.Item1].Y - 15); pointsAdded.Add(tp.Item1); } } y++; } } // 添加点到Chart foreach (int pt in points) { double dx = pointsToDraw[pt].X; double dy = pointsToDraw[pt].Y; chart1.Series[0].Points.AddXY(dx, dy); chart1.Series[0].Points[pt].Label = pt.ToString(); } // 保存数据到成员变量,供Paint事件使用 _currentPointsToDraw = pointsToDraw; _currentTree = Tree; } // 触发重绘,此时Paint只会画连线,不会再添加点 chart1.Invalidate(); }
步骤2:简化chart1_Paint事件(仅绘制连线)
把Paint方法里的所有数据操作逻辑删掉,只保留绘制连线的部分:
private void chart1_Paint(object sender, PaintEventArgs e) { // 只有当有数据时才绘制连线 if (_currentPointsToDraw?.Count > 1 && _currentTree != null) { using (Pen pen = new Pen(Color.Red, 2.5f)) { foreach (Tuple<int, int> tp in _currentTree) { Point[] eds = new Point[] { _currentPointsToDraw[tp.Item1], _currentPointsToDraw[tp.Item2] }; e.Graphics.DrawLines(pen, eds); } } } }
额外注意事项
- 原代码里有个小bug:在
points[i] == tp.Item2的分支中,你错误地给tp.Item2重复赋值,我已经在示例里修正为tp.Item1,你可以根据自己的业务逻辑调整。 - 使用
Contains替代Where(x => x == ...).Count() == 0,代码更简洁且性能更好。 - 确保
begX和begY是类成员变量,否则每次点击按钮都会重置初始坐标(如果这不是你想要的效果的话)。
内容的提问来源于stack exchange,提问作者PRHMN
相关产品推荐
相关产品推荐

