【问题标题】:What could cause Double Buffering to kill my app?什么可能导致双缓冲杀死我的应用程序?
【发布时间】:2012-02-26 09:32:11
【问题描述】:

我有一些使用 GDI+ 在屏幕上绘制的自定义(winforms)组件。

为了防止重绘时闪烁,我决定启用双缓冲,所以我在构造函数中添加了一行:

public ColourWheel()
{
    InitializeComponent();
    this.DoubleBuffered = true;
}

在这个组件(ColourWheel)上效果很好。当我将同一行添加到其他两个(结构相似)组件中的任何一个的构造函数中时,我得到了一些奇怪的症状:

  1. 当我尝试在打开组件的情况下运行表单时,Application.Run(new Form()); 上出现参数异常。
  2. 如果我切换到设计模式,我会收到有关组件存在与参数有关的未处理异常的错误消息。

我是否在其中一个或全部上打开双缓冲似乎并不重要,它仍然适用于 ColourWheel,但不适用于其他。

为了记录,我还尝试了一些其他 double buffering 技术。

什么可能导致双缓冲在一个组件上起作用,而在其他组件上不起作用?


编辑:这是运行时症状的异常详细信息:

System.ArgumentException 未处理 Message=Parameter is not 有效的。源 = System.Drawing StackTrace: 在 System.Drawing.Graphics.GetHdc() 在 System.Drawing.BufferedGraphics.RenderInternal(HandleRef refTargetDC,BufferedGraphics 缓冲区) 在 System.Drawing.BufferedGraphics.Render() 在 System.Windows.Forms.Control.WmPaint(消息和 m) 在 System.Windows.Forms.Control.WndProc(消息和 m) 在 System.Windows.Forms.ScrollableControl.WndProc(消息和 m) 在 System.Windows.Forms.UserControl.WndProc(消息和 m) 在 System.Windows.Forms.Control.ControlNativeWindow.OnMessage(消息& m) 在 System.Windows.Forms.Control.ControlNativeWindow.WndProc(消息和 m) 在 System.Windows.Forms.NativeWindow.DebuggableCallback(IntPtr hWnd,Int32 msg,IntPtr wparam,IntPtr lparam) 在 System.Windows.Forms.UnsafeNativeMethods.DispatchMessageW(味精和味精) 在 System.Windows.Forms.Application.ComponentManager.System.Windows.Forms.UnsafeNativeMethods.IMsoComponentManager.FPushMessageLoop(IntPtr dwComponentID, Int32 原因, Int32 pvLoopData) 在 System.Windows.Forms.Application.ThreadContext.RunMessageLoopInner(Int32 原因,ApplicationContext 上下文) 在 System.Windows.Forms.Application.ThreadContext.RunMessageLoop(Int32 原因,ApplicationContext 上下文) 在 System.Windows.Forms.Application.Run(窗体 mainForm) 在 D:\Documents and Settings\Tom Wright\My Documents\Visual Studio 中的 TestForm.Program.Main() 2010\Projects\ColourPicker\TestForm\Program.cs:第 18 行 在 System.AppDomain._nExecuteAssembly(RuntimeAssembly 程序集,字符串 [] 参数) 在 System.AppDomain.ExecuteAssembly(字符串 assemblyFile,证据 assemblySecurity,String [] args) 在 Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() 在 System.Threading.ThreadHelper.ThreadStart_Context(对象状态) 在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback 回调, 对象状态, Boolean 忽略SyncCtx) 在 System.Threading.ExecutionContext.Run(ExecutionContext executionContext,ContextCallback 回调,对象状态) 在 System.Threading.ThreadHelper.ThreadStart() InnerException:


编辑 2:来自导致问题的两个组件之一(更复杂)的 OnPaint 处理程序:

private void ValueSlider_Paint(object sender, PaintEventArgs e)
{
       using (Graphics g = e.Graphics)
       {
           g.DrawImage(this.gradientImage, new Rectangle(0, 0, paintArea.Width, paintArea.Height));
           if (this.showmarker)
           {
               ColourHandler.HSV alt = ColourHandler.RGBtoHSV(new ColourHandler.RGB(this.SelectedColour.R, this.SelectedColour.G, this.SelectedColour.B));
               alt.Saturation = 0;
               alt.value = 255 - alt.value;
               using (Pen pen = new Pen(ColourHandler.HSVtoColour(alt)))
               {
                   pen.Width = (float)MARKERWIDTH;
                   g.DrawRectangle(pen, 0 - pen.Width, this.brightnessPoint.Y - MARKERWIDTH, this.paintArea.Width + (pen.Width * 2), MARKERWIDTH * 2);
               }
           }
        }
}

【问题讨论】:

  • 您是否在有问题的控件中覆盖 OnPaint()?如果是这样,那是什么样的?
  • @roken 我正在编辑我的问题以包含我的 OnPaint 处理程序。

标签: c# components gdi+ double-buffering


【解决方案1】:

您不应该在Paint 事件期间处置借给您的Graphics 对象,这就是您的using 块不正确的做法。

症状是下次触发Paint 事件时,您会返回相同的Graphics 对象,但它不再绑定到内存中的HDC,导致Graphics.GetHdc() 失败,如所见在您的堆栈跟踪中。

  1. 它可能比单个 Paint 事件的寿命更长(这很可能是双缓冲的情况,但如果设置了 CS_OWNDC 窗口样式,单缓冲也可能出现这种情况)。

  2. Paint 事件可以有多个处理程序。

因此,事件处理程序不应在 Graphics 对象上调用 Dispose 或允许 using 块这样做。相反,.NET 框架会在 Paint 事件处理完成后适当地清理资源。

【讨论】:

  • 它工作了一年多,然后当我将用户控件类移动到它自己的文件时,开始抛出错误 OP here got。你的建议奏效了——C# 有时真的很奇怪!
  • 感谢您澄清这一点,MSDN 很困惑:您应该始终对任何消耗系统资源的对象调用 Dispose,例如 Pen 和 Graphics 对象。跨度>
  • @yano:当然,这仅意味着您创建的那些。销毁借来的物品对真正的主人很不友好。
【解决方案2】:

您应该在另一台机器上测试它,看看它是否只是您的计算机。在大多数情况下,这不应该由于双缓冲而发生,但请检查您是否正在处理任何您不应该在 Paint 事件中出现的元素,或者在代码中执行任何如果执行两次就会出现问题的事情。

【讨论】:

  • 我认为您不应该处置在Paint 事件期间提供给您的Graphics 对象。
  • @ben 是对的。不要在“g”上执行“使用”,因为这会在您退出该范围时释放 Graphics 对象,但您不是该对象的“所有者”。
  • 谢谢大家。我在做using (Graphics g = e.Graphics) { }。删除它是关键。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-01-10
  • 1970-01-01
  • 2014-07-11
  • 1970-01-01
  • 1970-01-01
  • 2011-04-29
相关资源
最近更新 更多