【问题标题】:Why does DrawImageUnscaled cause flickering when used from WM_NCPAINT?为什么从 WM_NCPAINT 使用 DrawImageUnscaled 会导致闪烁?
【发布时间】:2011-09-09 02:47:55
【问题描述】:

我目前正在构建一个从System.Windows.Forms.ContainerControl 派生的控件,它有一个我需要自己绘制的边界区域。由于没有要覆盖的OnPaintNonClientArea,所以我自己构建了它(为简洁起见,删除了WM_NCCALCSIZEWM_NCHITTEST 等其他消息的处理):

protected override void WndProc(ref Message m)
{
  switch (m.Msg)
  {
    case WM_NCPAINT:
      IntPtr hDC = NativeApi.Methods.GetWindowDC(m.HWnd);
      if (hDC != IntPtr.Zero)
      {
        using (Graphics canvas = Graphics.FromHdc(hDC))
        {
          if (Width > 0 && Height > 0)
            using (PaintEventArgs e = new PaintEventArgs(canvas, new Rectangle(0, 0, Width, Height)))
            {
              OnPaintNonClientArea(e);
            }
        }
        NativeApi.Methods.ReleaseDC(m.HWnd, hDC);
      }
      m.Result = IntPtr.Zero;
      break;
  }
  base.WndProc(ref m);
}

OnPaintNonClientArea,我做到了:

private void OnPaintNonClientArea(PaintEventArgs e)
{
  if (_ncBuffer == null)
  {
    _ncBuffer = new Bitmap(Width, Height);
  }

  using (Graphics g = Graphics.FromImage(_ncBuffer))
  {
    // painting occurs here ...
  }
  // this causes flickering
  e.Graphics.DrawImageUnscaled(_ncBuffer, 0, 0, Width, Height);
}

保持OnPaintNonClientArea 不变,这将消除闪烁:

protected override void WndProc(ref Message m)
{
  switch (m.Msg)
  {
    case WM_NCPAINT:
      using(Bitmap ncBitmap = new Bitmap(Width, Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb))
      {
        using(Graphics ncGraphics = Graphics.FromImage(ncBitmap))
        {
          using (PaintEventArgs e = new PaintEventArgs(ncGraphics, new Rectangle(0, 0, Width, Height)))
          {
            OnPaintNonClientArea(e);
            IntPtr hDCWin = NativeApi.Methods.GetWindowDC(m.HWnd);
            IntPtr hDCImg = ncGraphics.GetHdc();
            IntPtr hBmp = ncBitmap.GetHbitmap();
            IntPtr hBmpOld = NativeApi.Methods.SelectObject(hDCImg, hBmp);
            Padding p = GetNonClientArea();
            NativeApi.Methods.ExcludeClipRect(hDCWin, p.Left, p.Top,Width- p.Right, Height-p.Bottom);
            NativeApi.Methods.BitBlt(hDCWin, 0, 0, Width, Height, hDCImg, 0, 0,NativeApi.TernaryRasterOperations.SRCCOPY);
            NativeApi.Methods.SelectObject(hDCImg, hBmpOld);
            NativeApi.Methods.DeleteObject(hBmp);
            ncGraphics.ReleaseHdc(hDCImg);
            NativeApi.Methods.ReleaseDC(m.HWnd, hDCWin);
          }
        }
      }
      m.Result = IntPtr.Zero;
      break;
  }
  base.WndProc(ref m);
}

那么,为什么DrawImageUnscaled 会导致这种闪烁?在绘制缓冲区之前,它似乎用白色刷子擦除了它工作的区域。我在文档中没有找到任何澄清这个问题的东西。如果只是控件周围的一个小边框也没关系,但是NC区域内会显示文本,因此该区域清晰可见,因此闪烁确实可见且烦人。

相关问题:我是否正确地处理了原生 GDI 内容,或者是否存在我现在看不到的潜在问题?另外,在创建ncBitmap时,我使用的是控件的宽度和高度,但是GDI+是与分辨率无关的,会有什么问题吗?

【问题讨论】:

  • 您是否尝试过在表单中​​使用双缓冲?双缓冲应该处理诸如此类的图形问题。此外,是否有 SuspendLayout 和 PerformLayout 等效项可用于在加载图形对象之前阻止控件更新?
  • 谢谢。我摆弄这些东西很长一段时间并尝试了各种各样的东西,有一次我确实对没有任何影响的表单进行了双缓冲。我认为这不适用于这里,因为我已经有效地进行了双缓冲 - 我正在使用 Graphics 对象绘制所有内容,然后使用 DrawImageUnscaled 我尝试将缓冲区绘制到由 @ 创建的 Graphics 对象上987654334@。它是 DrawImageUnscaled 本身,它首先使用白色画笔擦除要绘制的区域,然后绘制 Graphics 对象的内容。不确定这是否可以解决。

标签: c# gdi+ gdi


【解决方案1】:

为避免在 UserControl 中闪烁,我在 BufferedGraphics 类中获得了更好的运气。

MSDN

这是一个选项吗?

【讨论】:

  • +1 表示我不知道的一种方法。但是对于绘制非客户区,这会导致与我尝试的第一种方法一样多的闪烁。
猜你喜欢
  • 2015-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-09-07
  • 2013-05-26
  • 1970-01-01
  • 2012-03-11
  • 2013-06-28
相关资源
最近更新 更多