C# 双缓冲在高负载时引发异常
我正在测试我的windows窗体用户控件。当应用程序负载较高时(假设应用程序占用超过1 GB),它会抛出内存不足或参数无效异常 下面是使用缓冲区绘制控件的代码C# 双缓冲在高负载时引发异常,c#,winforms,out-of-memory,doublebuffered,C#,Winforms,Out Of Memory,Doublebuffered,我正在测试我的windows窗体用户控件。当应用程序负载较高时(假设应用程序占用超过1 GB),它会抛出内存不足或参数无效异常 下面是使用缓冲区绘制控件的代码 protected override void OnPaint(PaintEventArgs e) { if (m_buffer == null || m_Redraw) { if (m_buffer != null) m_buffer.Dispose();
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);
}
内存不足异常发生在e.Graphics.DrawImage(m_缓冲区,Point.Empty)代码>
新位图(宽度、高度)出现参数无效异常代码>
注意:只有当应用程序负载超过1GB(例如
1.5 GB(带2GB RAM)
无缓冲的绘制控件不会引发任何异常,但会导致闪烁效果。下面是没有缓冲的代码
protected override void OnPaint(PaintEventArgs e)
{
this.SuspendLayout();
DrawUserControl(e.Graphics, ClientRectangle);
this.ResumeLayout();
base.OnPaint(e);
}
我希望我的控件在高负载下渲染时不闪烁。我不希望应用程序因我的控制而中断。请分享你的建议
与缓冲相关的编辑:
1) 不会在所有绘制事件期间创建缓冲区图像。它将在第一次创建,如果控件需要重新绘制。否则,将在控件上绘制现有缓冲区图像。这样可以避免对控件进行不必要的重新绘制
2) 若控件需要重新绘制,将使用新位图,因为现有缓冲区图像的大小和控件的当前大小可能会有所不同。即使使用现有图像,参数无效引发异常
尝试使用BufferedGraphicsContext
根据@taffer的回答,我尝试使用BufferedGraphicsContext
。它还抛出内存不足异常。我在下面发布堆栈跟踪
System.ComponentModel.Win32异常(0x80004005):存储不足
可用于处理此命令
在System.Drawing.BufferedGraphicsContext.CreateCompatibleDB(IntPtr hdc,
IntPtr hpal、Int32 ULWITH、Int32 ulHeight、IntPtr&ppvBits)
在System.Drawing.BufferedGraphicsContext.CreateBuffer(IntPtr src,
Int32偏移量x、Int32偏移量、Int32宽度、Int32高度)
位于System.Drawing.BufferedGraphicsContext.AllocBuffer(图形
targetGraphics、IntPtr targetDC、矩形targetRectangle)
在
System.Drawing.BufferedGraphicsContext.AllocBufferInTempManager(图形
targetGraphics、IntPtr targetDC、矩形targetRectangle)
在System.Drawing.BufferedGraphicsContext.Allocate(图形目标图形,矩形目标透视角)
首先,为什么在OnPaint
中调用SuspendLayout
和resumeayout
?仅当容器控件(如面板)包含具有停靠/对齐的子控件,并且您希望在多个步骤中调整容器大小时防止自动重新对齐时,才需要此选项
其次,有许多更好的内置双缓冲方法
选项1:
对于窗体
s、面板
s和其他常用控件,在大多数情况下,设置双缓冲
属性就足够了。它受保护,因此您应该创建一个派生类:
public class DoubleBufferedPanel : Panel
{
public DoubleBufferedPanel()
{
DoubleBuffered = true;
}
}
选项2:
调用SetStyle
:
public class DoubleBufferedPanel : Panel
{
public DoubleBufferedPanel()
{
SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
}
}
选项3:
在某些情况下,无法使用上述选项(例如,Windows的淡入淡出动画不能与双缓冲一起使用)。在这里,您可以使用OnPaint
中的BufferedGraphics
类:
protected override void OnPaint(PaintEventArgs e)
{
// creating a buffered context
using (BufferedGraphicsContext context = new BufferedGraphicsContext())
{
// creating a buffer for the original Graphics
Rectangle rect = new Rectangle(Point.Empty, Control.ClientSize);
using (BufferedGraphics bg = context.Allocate(e.Graphics, rect))
{
using (PaintEventArgs be = new PaintEventArgs(bg.Graphics, e.ClipRectangle))
{
// Draw your control here onto the buffer (applied to your post)
DrawUserControl(be.Graphics, rect);
base.OnPaint(be);
}
// copying the buffer onto the original Graphics
bg.Render(e.Graphics);
}
}
}
首先,为什么在OnPaint
中调用SuspendLayout
和resumeayout
?仅当容器控件(如面板)包含具有停靠/对齐的子控件,并且您希望在多个步骤中调整容器大小时防止自动重新对齐时,才需要此选项
其次,有许多更好的内置双缓冲方法
选项1:
对于窗体
s、面板
s和其他常用控件,在大多数情况下,设置双缓冲
属性就足够了。它受保护,因此您应该创建一个派生类:
public class DoubleBufferedPanel : Panel
{
public DoubleBufferedPanel()
{
DoubleBuffered = true;
}
}
选项2:
调用SetStyle
:
public class DoubleBufferedPanel : Panel
{
public DoubleBufferedPanel()
{
SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint | ControlStyles.UserPaint, true);
}
}
选项3:
在某些情况下,无法使用上述选项(例如,Windows的淡入淡出动画不能与双缓冲一起使用)。在这里,您可以使用OnPaint
中的BufferedGraphics
类:
protected override void OnPaint(PaintEventArgs e)
{
// creating a buffered context
using (BufferedGraphicsContext context = new BufferedGraphicsContext())
{
// creating a buffer for the original Graphics
Rectangle rect = new Rectangle(Point.Empty, Control.ClientSize);
using (BufferedGraphics bg = context.Allocate(e.Graphics, rect))
{
using (PaintEventArgs be = new PaintEventArgs(bg.Graphics, e.ClipRectangle))
{
// Draw your control here onto the buffer (applied to your post)
DrawUserControl(be.Graphics, rect);
base.OnPaint(be);
}
// copying the buffer onto the original Graphics
bg.Render(e.Graphics);
}
}
}
尽量避免一次加载所有文件?一部分一部分地加载它们并与启动屏幕结合?您不需要每次绘制时都处理和重新创建缓冲区。只需清除它。创建位图非常昂贵。。你可能每秒要做几百次。将您的缓冲区分配到任何高流量区域之外。此外,大多数控件都有一个双缓冲区标志,您可以在.ctor via.Side中设置。注意:SuspendLayout()
和resumeloayout()
与绘制无关。绘制控件时不应触发布局事件
,否则控件将非常慢。您应该删除这些行。是否尝试避免一次加载所有行?一部分一部分地加载它们并与启动屏幕结合?您不需要每次绘制时都处理和重新创建缓冲区。只需清除它。创建位图非常昂贵。。你可能每秒要做几百次。将您的缓冲区分配到任何高流量区域之外。此外,大多数控件都有一个双缓冲区标志,您可以在.ctor via.Side中设置。注意:SuspendLayout()
和resumeloayout()
与绘制无关。绘制控件时不应触发布局事件
,否则控件将非常慢。您应该删除这些行。谢谢您的回答,选项1和2将不会有用,因为我的控件是从控件类派生的。我会尝试选项3选项3也会引发异常,我会在我的问题中添加详细信息谢谢你的回答,选项1和2不会有用,因为我的控件来自Co