执行图片框绘制事件处理程序变得越来越慢

本文关键字:越来越 程序 事件处理 绘制 执行 | 更新日期: 2023-09-27 18:03:30

请原谅我的英语很差。我试图在picturebox上绘制一条旋转线,通过在timer tick handler函数中设置Paint事件处理程序:

private void timer1_Tick(object sender, EventArgs e)
    {
        pictureBox1.Invalidate();
        pictureBox1.Paint += new PaintEventHandler(Draw);//1
        foreach (Line line in lines)//array "lines" contains just 16 objects
        {
            //calculating new coordinates ...
        }
    }

标记为"1"的行,随着时间的推移,执行速度越来越慢。下面是"绘制"函数代码:

void Draw(object sender, PaintEventArgs e)
    {
        foreach (Line line in lines)
        {
            e.Graphics.DrawLine(new Pen(Brushes.Black, 5f), line.P1, line.P2);
            e.Graphics.FillEllipse(Brushes.Red, line.P1.X - 2.5f, line.P1.Y - 2.5f, 5, 5);
            e.Graphics.FillEllipse(Brushes.Red, line.P2.X - 2.5f, line.P2.Y - 2.5f, 5, 5);
            e.Graphics.DrawEllipse(new Pen(Brushes.Red, 5f), line.P1.X, line.P1.Y, 1, 1);
            e.Graphics.DrawEllipse(new Pen(Brushes.Red, 5f), line.P2.X, line.P2.Y, 1, 1);
        }
    }

你们能告诉我,我该如何解决这个问题吗?谢谢!

执行图片框绘制事件处理程序变得越来越慢

不要做pictureBox1。Paint += new PaintEventHandler(Draw);//1 in timer1_Tick。在表单加载中执行一次。否则,它将在每个Paint事件中调用Draw()多次,并且调用的次数将增加。

试试这个

private void timer1_Tick(object sender, EventArgs e)
    {
        Graphics g = pictureBox1.CreateGraphics();
        foreach (Point line in lines)
        {
            g.DrawLine(new Pen(Brushes.Black, 5f), line.P1, line.P2);
            g.FillEllipse(Brushes.Red, line.P1.X - 2.5f, line.P1.Y - 2.5f, 5, 5);
            g.FillEllipse(Brushes.Red, line.P2.X - 2.5f, line.P2.Y - 2.5f, 5, 5);
            g.DrawEllipse(new Pen(Brushes.Red, 5f), line.P1.X, line.P1.Y, 1, 1);
            g.DrawEllipse(new Pen(Brushes.Red, 5f), line.P2.X, line.P2.Y, 1, 1);
        }
    }

如果不显示效果,则在循环

后添加以下行
pictureBox1.Invalidate();

除了解决其他答案中已经指出的事件处理程序问题外,我还将更改代码中的一些内容。

首先,为什么你要创造这么多的Pen ?让它们跳出循环:

void Draw(object sender, PaintEventArgs e)
{
    var blackPen = new Pen(Brushes.Black, 5f);
    var redPen = new Pen(Brushes.Red, 5f);
    foreach (Line line in lines)
    {
        e.Graphics.DrawLine(blackPen, line.P1, line.P2);
        e.Graphics.FillEllipse(Brushes.Red, line.P1.X - 2.5f, line.P1.Y - 2.5f, 5, 5);
        e.Graphics.FillEllipse(Brushes.Red, line.P2.X - 2.5f, line.P2.Y - 2.5f, 5, 5);
        e.Graphics.DrawEllipse(redPen, line.P1.X, line.P1.Y, 1, 1);
        e.Graphics.DrawEllipse(redPen, line.P2.X, line.P2.Y, 1, 1);
    }
}

嗯,现在,请注意Pen实现了IDisposable。一旦你用完这类东西,就要养成处理它们的习惯;这样,您将确定地释放底层非托管资源,而不是当GC决定:

void Draw(object sender, PaintEventArgs e)
{
    using (var blackPen = new Pen(Brushes.Black, 5f))
    using (var redPen = new Pen(Brushes.Red, 5f))
    {
        foreach (Line line in lines)
        {
            e.Graphics.DrawLine(blackPen, line.P1, line.P2);
            e.Graphics.FillEllipse(Brushes.Red, line.P1.X - 2.5f, line.P1.Y - 2.5f, 5, 5);
            e.Graphics.FillEllipse(Brushes.Red, line.P2.X - 2.5f, line.P2.Y - 2.5f, 5, 5);
            e.Graphics.DrawEllipse(redPen, line.P1.X, line.P1.Y, 1, 1);
            e.Graphics.DrawEllipse(redPen, line.P2.X, line.P2.Y, 1, 1);
        }
    }
}

此外,您甚至可以考虑将Pen缓存为实例变量;

class MyControl: ..., IDisposable
{
     private readonly Pen blackPen = new Pen(Brushes.Black, 5f));
     private readonly Pen redPen = new Pen(Brushes.Red, 5f));
     void Draw(object sender, PaintEventArgs e)
     {
         foreach (Line line in lines)
         {
            e.Graphics.DrawLine(blackPen, line.P1, line.P2);
            e.Graphics.FillEllipse(Brushes.Red, line.P1.X - 2.5f, line.P1.Y - 2.5f, 5, 5);
            e.Graphics.FillEllipse(Brushes.Red, line.P2.X - 2.5f, line.P2.Y - 2.5f, 5, 5);
            e.Graphics.DrawEllipse(redPen, line.P1.X, line.P1.Y, 1, 1);
            e.Graphics.DrawEllipse(redPen, line.P2.X, line.P2.Y, 1, 1);
        }
    }
    private void Dispose(bool disposing)
    {
         ....
         if (disposing)
         {
             ....
             blackPen.Dispose(;)
             redPen.Dispose();
         }
    }
}

看起来你的问题是pictureBox1.Paint += new PaintEventHandler(Draw);,所以当paint事件触发第一个tick时它在第二个tick上不做任何事情它在第500个tick上重绘一次它重绘499次

你需要做的是

private void configure()
{
    pictureBox1.Paint += new PaintEventHandler(pictureBox1_OnPaint);
}
private void timer1_Tick(object sender, EventArgs e)
{
    pictureBox1.Invalidate();
}
private void pictureBox1_OnPaint(object sender, PaintEventArgs e)
{
    foreach (Line line in lines)//array "lines" contains just 16 objects
    {
        //calculating new coordinates ...
        e.Graphics.DrawLine(new Pen(Brushes.Black, 5f), line.P1, line.P2);
        e.Graphics.FillEllipse(Brushes.Red, line.P1.X - 2.5f, line.P1.Y - 2.5f, 5, 5);
        e.Graphics.FillEllipse(Brushes.Red, line.P2.X - 2.5f, line.P2.Y - 2.5f, 5, 5);
        e.Graphics.DrawEllipse(new Pen(Brushes.Red, 5f), line.P1.X, line.P1.Y, 1, 1);
        e.Graphics.DrawEllipse(new Pen(Brushes.Red, 5f), line.P2.X, line.P2.Y, 1, 1);
    }
}

的一些细节,因为你显然不理解事件处理程序是如何工作的

pictureBox1.Paint += new PaintEventHandler(pictureBox1_OnPaint);

读取为绘制事件发生时,我希望你调用pictureBox1_OnPaint每次调用该命令时,你添加一个额外的实例,该方法调用到您的程序,所以

pictureBox1.Paint += new PaintEventHandler(pictureBox1_OnPaint);
pictureBox1.Paint += new PaintEventHandler(pictureBox1_OnPaint);

表示当Paint发生时,您希望调用pictureBox1_OnPaint两次。它永远不会触发事件,调用Invalidate()确实会触发事件。

所以你需要做的是,当窗体被初始化时(可以是构造函数,初始化事件,如(form . load)或类似的地方)告诉窗体,当它在

发生时,你希望它对paint事件做什么

pictureBox1.Paint += new PaintEventHandler( action );

但只做一次

然后在计时器上调用Invalidate以触发事件

因为每次paint事件触发时tick上的逻辑都会发生那么您不妨将其添加到handers操作中而不是两次执行lines集合