我有 3 个通过其绘制事件绘制的数据图。当我有需要插入图表的数据时,我调用控件 invalidate() 命令。
第一个控件的绘制事件实际上为其他 2 个图形创建了一个位图缓冲区,以避免重复长循环。
因此,无效命令按特定顺序 (1,2,3)。这很有效,但是当图形数据到达图形窗口 (PictureBox) 的末尾时,数据通常会开始滚动,绘制事件开始以错误的顺序 (2,3,1) 触发。
有没有人遇到过这个?为什么会发生这种情况?
我有 3 个通过其绘制事件绘制的数据图。当我有需要插入图表的数据时,我调用控件 invalidate() 命令。
第一个控件的绘制事件实际上为其他 2 个图形创建了一个位图缓冲区,以避免重复长循环。
因此,无效命令按特定顺序 (1,2,3)。这很有效,但是当图形数据到达图形窗口 (PictureBox) 的末尾时,数据通常会开始滚动,绘制事件开始以错误的顺序 (2,3,1) 触发。
有没有人遇到过这个?为什么会发生这种情况?
更改代码,以便在调用Invalidate
三个控件中的任何一个之前,创建一个共享位图缓冲区(可以想象为控件类的静态成员),然后调用Invalidate
每个控件。在控件的事件中,您可以使用静态位图缓冲区,并且事件触发Paint
的顺序无关紧要。Paint
当您调用Invalidate
控件时,您基本上是在告诉操作系统向该控件发送 WM_PAINT 消息。因为它是一条 Windows 消息,所以只要 Windows 开始执行它,就可以保证它会被传递。在您的情况下,它们通常按收到的顺序交付,但有时它们不会。
代码要考虑的另一件事是:当您将相对复杂的绘图代码放置在控件的Paint
事件处理程序中(或直接从事件处理程序调用的方法中)时,只要控件因任何原因Paint
无效,该代码就会执行,这意味着代码将在您调用时运行,但它也会在将另一个窗口拖到控件上时运行。Invalidate
对于复杂、耗时的图形,最好在隐藏缓冲区(Bitmap
或不可见PictureBox
或其他)上执行复杂的渲染,然后在控件的Paint
事件中从隐藏缓冲区简单复制到可见窗口(使用Graphics.DrawImage
或BitBlt
管他呢)。
如果您在绘制的缓冲区和可见窗口之间添加第二个缓冲区(因此是“双缓冲”),这种方法还允许您避免闪烁。在主缓冲区上完成绘图后,将其复制到第二个缓冲区。在控件的Paint
事件中,您从第二个缓冲区复制到可见窗口。
因为调用Invalidate()
控件本质上是要求操作系统安排控件进行重绘,所以不能保证这是按特定顺序完成的。
由于您尝试按Invalidate()
特定顺序调用,我假设您只有一个方法可以执行此操作。您可以在这些调用之前添加代码以绘制位图缓冲区,然后由无效控件使用。我不知道这是否会发生,但这也使您可以自由地在数据不变时不使控件无效。此外,控件可以随时失效。例如,跨控件 1 移动表单会导致它无效,并且在您当前的设置中,重新计算缓冲区位图,而这不是必需的。因此,您应该将此功能分开。