C# 双缓冲在高负载时引发异常

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();

我正在测试我的windows窗体用户控件。当应用程序负载较高时(假设应用程序占用超过1 GB),它会抛出内存不足参数无效异常

下面是使用缓冲区绘制控件的代码

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