I am testing my windows forms user control. When application load is high (say application takes more than 1 GB), it throws Out of memory or Argument invalid exceptions.
Here is the code to draw the control with buffer,
protected override void OnPaint(PaintEventArgs e)
{
if (m_buffer == null || m_Redraw)
{
if (m_buffer != null)
m_buffer.Dispose();
m_buffer = new Bitmap(Width, Height);
using (Graphics g = Graphics.FromImage(m_buffer))
{
DrawUserControl(g, ClientRectangle);
}
m_Redraw= false;
}
e.Graphics.DrawImage(m_buffer, Point.Empty);
base.OnPaint(e);
}
Out of memory exception occurs at e.Graphics.DrawImage(m_buffer, Point.Empty);
Argument invalid exception occurs at new Bitmap(Width, Height);
Note: Exception occurs only if application load is more that 1 GB (say 1.5 GB with 2GB RAM).
Painting control without buffering does not throw any exception but causes flickering effect. Here is code without buffering
protected override void OnPaint(PaintEventArgs e)
{
this.SuspendLayout();
DrawUserControl(e.Graphics, ClientRectangle);
this.ResumeLayout();
base.OnPaint(e);
}
I want my control to render without flickering under high load. I don't want the application to break due to my control. Please share your suggestions
Edit related to buffering:
1) Buffer image will not be created during all paint events. It will be created during the first time and if control needs to be repainted. Otherwise, existing buffer image will be drawn on the control. This avoids unwanted repainting of the control
2) If control needs to be repainted, a new bitmap will be used because size of the existing buffer image and current size of the control may vary. Even if I use existing image, Argument is not valid exception thrown
Tried using BufferedGraphicsContext
As per @taffer's answer, I tried using BufferedGraphicsContext. It also throws Out of memory exception. I'm posting the stack trace below
System.ComponentModel.Win32Exception (0x80004005): Not enough storage is available to process this command at System.Drawing.BufferedGraphicsContext.CreateCompatibleDIB(IntPtr hdc, IntPtr hpal, Int32 ulWidth, Int32 ulHeight, IntPtr& ppvBits)
at System.Drawing.BufferedGraphicsContext.CreateBuffer(IntPtr src, Int32 offsetX, Int32 offsetY, Int32 width, Int32 height)
at System.Drawing.BufferedGraphicsContext.AllocBuffer(Graphics targetGraphics, IntPtr targetDC, Rectangle targetRectangle)
at System.Drawing.BufferedGraphicsContext.AllocBufferInTempManager(Graphics targetGraphics, IntPtr targetDC, Rectangle targetRectangle) at System.Drawing.BufferedGraphicsContext.Allocate(Graphics targetGraphics, Rectangle targetRectangle)
Firstly, why do you call
SuspendLayoutandResumeLayoutinOnPaint? It is required only if a container control (eg. Panel) contains child controls with docking/alignments and you want to prevent automatic re-alignments when the container is resized in more than one steps.Secondly, there are many better built-in ways for double-buffering.
Option 1:
For
Forms,Panels and other usual controls in most cases it is enough to set theDoubleBufferedproperty. It is protected so you should create a derived class:Option 2:
Call
SetStyle:Option 3:
There are some cases when the options above cannot be used (for example, fading animations of Windows cannot be used with double buffering). Here you can use the
BufferedGraphicsclass inOnPaint: