我正在通过System.Drawing进行一些小图表,我遇到了一些问题。
我在队列中保存数据,我正在将这些数据绘制(绘制)到三个图片框上
此方法填充图片框,然后滚动图表。
所以不要在之前的图纸上画画(并且看起来更加混乱)我找到了2个绘制图形的解决方案。
plot.Clear(BACKGOUNDCOLOR)
[块已注释] 虽然这会导致从执行实际绘图循环所需的时间出现闪烁。
plot.DrawLine(channelPen[5], j, 140, j, 0);
就在每条画线前[评论] 虽然这会导致绘图开始正常,然后非常快地减速到爬行,就像在绘制命令之前放置了等待命令一样。
以下是供参考的代码:
/*plotx.Clear(BACKGOUNDCOLOR)
ploty.Clear(BACKGOUNDCOLOR)
plotz.Clear(BACKGOUNDCOLOR)*/
for (int j = 1; j < 599; j++)
{
if (j > RealTimeBuffer.Count - 1) break;
QueueEntity past = RealTimeBuffer.ElementAt(j - 1);
QueueEntity current = RealTimeBuffer.ElementAt(j);
if (j == 1)
{
//plotx.DrawLine(channelPen[5], 0, 140, 0, 0);
//ploty.DrawLine(channelPen[5], 0, 140, 0, 0);
//plotz.DrawLine(channelPen[5], 0, 140, 0, 0);
}
//plotx.DrawLine(channelPen[5], j, 140, j, 0);
plotx.DrawLine(channelPen[0], j - 1, (((past.accdata.X - 0x7FFF) / 256) + 64), j, (((current.accdata.X - 0x7FFF) / 256) + 64));
//ploty.DrawLine(channelPen[5], j, 140, j, 0);
ploty.DrawLine(channelPen[1], j - 1, (((past.accdata.Y - 0x7FFF) / 256) + 64), j, (((current.accdata.Y - 0x7FFF) / 256) + 64));
//plotz.DrawLine(markerPen, j, 140, j, 0);
plotz.DrawLine(channelPen[2], j - 1, (((past.accdata.Z - 0x7FFF) / 256) + 94), j, (((current.accdata.Z - 0x7FFF) / 256) + 94));
}
是否有任何技巧可以避免这些开销?
如果没有,是否会有其他/更好的解决方案?
编辑:有关解决方案代码,请参阅下面的[最终解决方案]
答案 0 :(得分:4)
PictureBox已启用双缓冲。您可以感知闪烁的唯一方法是直接绘制到屏幕而不是使用Paint事件。目前尚不清楚你是否使用你的代码片段。使用位图缓冲自己也会起作用,但效率不如Windows Forms实现的双缓冲。
当数据更改时调用其Invalidate()方法,在Paint事件中执行绘图(使用e.Graphics),它不会闪烁。
答案 1 :(得分:1)
尝试将数据缓冲到另一个位图,然后再将其放入图片框中。这将消除闪烁。
例如:
// create this once for each graph
Bitmap buffer = new Bitmap({width}, {height});
// when the data changes
using (Graphics g = Graphics.FromImage(buffer))
{
// ... draw using the graphics
}
// draw the buffer on the picture box's image
你是在一个单独的线程上进行绘图吗?你一次抽取数据多少次?如果你在主线程上执行它或者每秒重复绘制整个位图,你的程序会感觉很慢。
答案 2 :(得分:0)
我会使用双缓冲方法。在内存中创建一个新的位图(与图形大小相同)创建该位图的图形对象。
然后在该对象上绘制您想要的任何内容。
完成所有绘图后,从pictureBox创建另一个图形对象,并使用DrawImage绘制内存位图。
这样可以减少闪烁,因为除了一个更新整个图像的大绘图外,您绘制了屏幕的所有内容。
希望它有所帮助。
答案 3 :(得分:0)
对图片框进行子类化并启用双缓冲区选项。
公共类MyBufferedPictureBox 继承System.Windows.Forms.PictureBox
Public Sub New()
MyBase.New()
Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
End Sub
结束班
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.setstyle.aspx
答案 4 :(得分:0)
[最终解决方案]
即使使用重复循环也使用paint事件足够快!
在结束而不是在每个事件中复制绘图循环时,我使用第一个事件将另外两个图形绘制到位图,然后在它们的绘制事件上,只需绘制位图。
所以基本上最终的解决方案是你的许多答案的组合。
谢谢你们。
private void pictureBoxAccX_Paint(object sender, PaintEventArgs e)
{
bufferedploty.Clear(Color.Black);
bufferedplotz.Clear(Color.Black);
for (int j = 1; j < 599; j++)
{
if (j > RealTimeBuffer.Count - 1) break;
QueueEntity past = RealTimeBuffer.ElementAt(j - 1);
QueueEntity current = RealTimeBuffer.ElementAt(j);
e.Graphics.DrawLine(channelPen[0], j - 1, (((past.accdata.X - 0x7FFF) / 256) + 64), j, (((current.accdata.X - 0x7FFF) / 256) + 64));
bufferedploty.DrawLine(channelPen[1], j - 1, (((past.accdata.Y - 0x7FFF) / 256) + 64), j, (((current.accdata.Y - 0x7FFF) / 256) + 64));
bufferedplotz.DrawLine(channelPen[2], j - 1, (((past.accdata.Z - 0x7FFF) / 256) + 94), j, (((current.accdata.Z - 0x7FFF) / 256) + 94));
}
}
private void pictureBoxAccY_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(BufferedBitMapy, new Point(0, 0));
}
private void pictureBoxAccZ_Paint(object sender, PaintEventArgs e)
{
e.Graphics.DrawImage(BufferedBitMapz, new Point(0, 0));
}
private void AddAccPoints()
{
//Code for putting in New queue data Here...
pictureBoxAccX.Invalidate();
pictureBoxAccY.Invalidate();
pictureBoxAccZ.Invalidate();
}
编辑:使用这个精确的解决方案使控件无效可能导致事件调度未定义并随机停止绘制,因为位图创建是在一个事件方法中完成的。
见这里:Onpaint events (invalidated) changing execution order after a period normal operation (runtime)