You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多窗体绘制大量线条性能问题:200+线条耗时约1秒求排查

嘿,看了你的代码,能猜到为啥绘制200多条线就卡1秒了——这里有不少可以优化的点,咱们一个个拆解:

1. 重复的属性/方法调用,纯纯浪费性能

你在循环里反复调用objects[i].getEdges()、getPoint1()、getPoint2(),这些每次调用都可能产生额外的开销(比如方法调用的栈操作,或是属性内部的额外逻辑)。应该把这些值提前缓存到局部变量里,减少重复计算。

2. 冗余的条件判断完全没必要

你在遍历ed的循环里加了if (objects[i].getEdges().Count > 0),但既然ed是从0到objects[i].getEdges().Count - 1,这个判断完全多余,只会增加循环里的分支开销,直接删掉就行。

3. 坐标计算重复做,白耗CPU

像X + 332、-(Y - 170)这种坐标转换,你每个绘制调用都算一遍,完全可以提前把转换后的点坐标算好,存到临时变量里,不用每次绘制都重复计算。

4. Pen的切换可以提前搞定

你现在在每个edge的循环里判断objects[i].selected来切换pAux,但这个值是针对整个object的,完全可以在遍历objects[i]的时候就确定好要用的Pen,不用在每个edge里重复判断。

5. 别用单个绘制API,试试批量调用

GDI+的单个DrawLine/DrawRectangle调用开销其实不小。如果能把一组线的点收集起来,用DrawLines代替多次DrawLine,或者用GraphicsPath把所有要画的图形加进去最后一次性绘制,能大幅减少API调用次数,提升性能。

6. 开启双缓冲解决闪烁+提速

WinForms的控件默认可能没开启双缓冲,这会导致绘制时的闪烁,也会间接影响绘制速度。你可以在窗体/控件的构造函数里加上DoubleBuffered = true;,或者手动实现双缓冲。


优化后的代码示例

先给你一个基础优化版本,解决前面提到的前几个问题:

public void drawObjects()
{
    // 提前缓存常用Pen,减少重复赋值
    Pen linePenCache = linePen;
    Pen selectedPenCache = selectedPen;

    foreach (var obj in objects)
    {
        // 针对当前Object确定Pen,只判断一次
        Pen currentPen = obj.selected ? selectedPenCache : linePenCache;
        var edges = obj.getEdges(); // 缓存edges集合,避免重复调用方法

        foreach (var edge in edges)
        {
            // 缓存两个端点,避免重复调用getPoint1/getPoint2
            var point1 = edge.getPoint1();
            var point2 = edge.getPoint2();

            // 提前计算三个视图的坐标,避免重复运算
            // 前视图坐标
            int frontX1 = point1.X + 332;
            int frontY1 = -(point1.Y - 170);
            int frontX2 = point2.X + 332;
            int frontY2 = -(point2.Y - 170);

            // 右视图坐标
            int rightX1 = point1.Z + 332;
            int rightY1 = -(point1.Y - 170);
            int rightX2 = point2.Z + 332;
            int rightY2 = -(point2.Y - 170);

            // 顶视图坐标
            int topX1 = point1.X + 332;
            int topY1 = -(point1.Z - 170);
            int topX2 = point2.X + 332;
            int topY2 = -(point2.Z - 170);

            // 绘制端点矩形
            frontGraphics.DrawRectangle(recPen, frontX1, frontY1, 2, 2);
            rightGraphics.DrawRectangle(recPen, rightX1, rightY1, 2, 2);
            topGraphics.DrawRectangle(recPen, topX1, topY1, 2, 2);

            // 绘制线条
            frontGraphics.DrawLine(currentPen, frontX1, frontY1, frontX2, frontY2);
            rightGraphics.DrawLine(currentPen, rightX1, rightY1, rightX2, rightY2);
            topGraphics.DrawLine(currentPen, topX1, topY1, topX2, topY2);
        }
    }
}

进阶优化:批量绘制

如果想进一步提速,可以把相同Pen的线条/矩形批量收集,再一次性调用绘制API(注意如果不同Object用不同Pen,需要按Pen分组收集):

public void drawObjects()
{
    // 按视图和Pen分组收集绘制数据
    Dictionary<Pen, List<Point>> frontLines = new Dictionary<Pen, List<Point>>();
    Dictionary<Pen, List<Point>> rightLines = new Dictionary<Pen, List<Point>>();
    Dictionary<Pen, List<Point>> topLines = new Dictionary<Pen, List<Point>>();

    List<Rectangle> frontRects = new List<Rectangle>();
    List<Rectangle> rightRects = new List<Rectangle>();
    List<Rectangle> topRects = new List<Rectangle>();

    foreach (var obj in objects)
    {
        Pen currentPen = obj.selected ? selectedPen : linePen;
        var edges = obj.getEdges();

        // 初始化当前Pen的点列表(如果不存在)
        if (!frontLines.ContainsKey(currentPen))
        {
            frontLines[currentPen] = new List<Point>();
            rightLines[currentPen] = new List<Point>();
            topLines[currentPen] = new List<Point>();
        }

        foreach (var edge in edges)
        {
            var point1 = edge.getPoint1();
            var point2 = edge.getPoint2();

            // 前视图数据收集
            int fx1 = point1.X + 332;
            int fy1 = -(point1.Y - 170);
            int fx2 = point2.X + 332;
            int fy2 = -(point2.Y - 170);
            frontLines[currentPen].Add(new Point(fx1, fy1));
            frontLines[currentPen].Add(new Point(fx2, fy2));
            frontRects.Add(new Rectangle(fx1, fy1, 2, 2));

            // 右视图数据收集
            int rx1 = point1.Z + 332;
            int ry1 = -(point1.Y - 170);
            int rx2 = point2.Z + 332;
            int ry2 = -(point2.Y - 170);
            rightLines[currentPen].Add(new Point(rx1, ry1));
            rightLines[currentPen].Add(new Point(rx2, ry2));
            rightRects.Add(new Rectangle(rx1, ry1, 2, 2));

            // 顶视图数据收集
            int tx1 = point1.X + 332;
            int ty1 = -(point1.Z - 170);
            int tx2 = point2.X + 332;
            int ty2 = -(point2.Z - 170);
            topLines[currentPen].Add(new Point(tx1, ty1));
            topLines[currentPen].Add(new Point(tx2, ty2));
            topRects.Add(new Rectangle(tx1, ty1, 2, 2));
        }
    }

    // 批量绘制矩形
    if (frontRects.Count > 0)
    {
        frontGraphics.DrawRectangles(recPen, frontRects.ToArray());
        rightGraphics.DrawRectangles(recPen, rightRects.ToArray());
        topGraphics.DrawRectangles(recPen, topRects.ToArray());
    }

    // 批量绘制线条(按Pen分组绘制)
    foreach (var pair in frontLines)
    {
        if (pair.Value.Count >= 2)
            frontGraphics.DrawLines(pair.Key, pair.Value.ToArray());
    }
    foreach (var pair in rightLines)
    {
        if (pair.Value.Count >= 2)
            rightGraphics.DrawLines(pair.Key, pair.Value.ToArray());
    }
    foreach (var pair in topLines)
    {
        if (pair.Value.Count >= 2)
            topGraphics.DrawLines(pair.Key, pair.Value.ToArray());
    }
}

最后别忘了给你的绘制控件开启双缓冲——在控件构造函数里设置DoubleBuffered = true;,或者重写OnPaintBackground方法为空(避免重复绘制背景),这能解决闪烁问题,也能进一步提升绘制流畅度。

内容的提问来源于stack exchange,提问作者Dazentt Eduardo Dazentt55

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 09:46:30