为什么从WM_NCPAINT使用DrawImageUnscaled会导致闪烁

本文关键字:闪烁 DrawImageUnscaled 使用 WM NCPAINT 为什么 | 更新日期: 2023-09-27 17:58:39

我目前正在构建一个从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+是独立于分辨率的,会有什么问题吗?

为什么从WM_NCPAINT使用DrawImageUnscaled会导致闪烁

为了避免UserControl中的闪烁,我最好使用BufferedGraphics类。

MSDN

这是一种选择吗?