Three System.Drawing methods manifest slow drawing or flickery: Solutions? or Other Options?
I am doing a little graphing via the System.Drawing and im having a few problems.
I'm holding data in a Queue and i'm drawing(graphing) out that data onto three picture boxes
this method fills the picture box then scrolls the graph across.
so not to draw on top of the previous drawings (and graduly looking messier) i found 2 solutions to draw the graph.
- Call
plot.Clear(BACKGOUNDCOLOR)
before the draw loop [block commented]
although this causes a flicker to appear from the time it takes to do the actual drawing loop.
- call
plot.DrawLine(channelPen[5], j, 140, j, 0);
just before each drawline [commented]
although this causes the drawing to start ok then slow down very quickly to a crawl as if a wait command had been placed before the draw command.
Here is the Code for reference:
/*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));
}
Is there any tricks to avoid these overheads?
If not would there be any other/better solutions?
EDIT: See [Final Solution] below for solution code
PictureBox already has double-buffering turned on. The only way you could perceive flicker is when you draw directly to the screen instead of using the Paint event. It isn't clear whether you do from your snippet. Buffering yourself with a bitmap would work too but isn't as efficient as the double-buffering implemented by Windows Forms.
Call its Invalidate() method when the data changes, do the drawing in the Paint event (using e.Graphics) and it won't flicker.
Try buffering your data to another bitmap before you put it in the picture box. That will eliminate the flicker.
For example:
// 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
Are you doing the drawing on a separate thread? How many times are you drawing the data a second? Your program will feel slow if you are doing it on the main thread or are repeatedly drawing the entire bitmap a number of times a second.
Subclass your picture box and enable the double buffer option.
Public Class MyBufferedPictureBox Inherits System.Windows.Forms.PictureBox
Public Sub New()
MyBase.New()
Me.SetStyle(ControlStyles.OptimizedDoubleBuffer, True)
End Sub
End Class
http://msdn.microsoft.com/en-us/library/system.windows.forms.control.setstyle.aspx
I would use a double buffer approach. Create a new bitmap in memory(the same size as the graph) create a graphics object of that bitmap.
Then draw whatever you want on that object.
After all the drawing is done then create another graphics object from the pictureBox and draw the in-memory-bitmap using DrawImage.
This should reduce the flickering since you draw everything ofscreen except one big draw that updates the whole image.
Hope it helps.
[FINAL SOLUTION]
Using the paint event even with duplicate loops was fast enough!
In the End rather than duplicate the drawing loop in each event, I used the first event to also draw the other two graphs to bitmaps and then on their paint event, simply drew the bitmap.
So essentially the final solution was a combination of many of your answers.
Thank guys.
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();
}
EDIT: With this exact solution invalidating the controls can cause the event scheduling to be undefined and randomly stop drawing as the bitmap creation is done in one the event methods.
See here: Onpaint events (invalidated) changing execution order after a period normal operation (runtime)
精彩评论