图片框绘制事件处理程序的执行变得越来越慢
Executing of picture box paint event handler becomes slower and slower
请原谅我的英语不好。我试图通过在计时器滴答处理函数中设置 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”的行随着时间的推移执行得越来越慢。下面是 "Draw" 函数代码:
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);
所以当 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)这样的初始化事件或类似的地方告诉表单你想要它做什么当
发生时绘制事件
pictureBox1.Paint += new PaintEventHandler( action );
但只做一次
然后在你的计时器上调用 Invalidate
以触发事件
由于每次绘制事件触发时都会发生刻度逻辑,因此您最好将其添加到处理程序操作中,而不是两次遍历行集合
不要执行 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();
}
}
}
请原谅我的英语不好。我试图通过在计时器滴答处理函数中设置 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”的行随着时间的推移执行得越来越慢。下面是 "Draw" 函数代码:
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);
所以当 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)这样的初始化事件或类似的地方告诉表单你想要它做什么当
发生时绘制事件pictureBox1.Paint += new PaintEventHandler( action );
但只做一次
然后在你的计时器上调用 Invalidate
以触发事件
由于每次绘制事件触发时都会发生刻度逻辑,因此您最好将其添加到处理程序操作中,而不是两次遍历行集合
不要执行 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();
}
}
}