Why does DrawImageUnscaled cause flickering when used from WM

I'm currently building a control derived from System.Windows.Forms.ContainerControl that has a border area I need to paint myself. Since there's no OnPaintNonClientArea to override, I built it myself like this (handling of other messages like WM_NCCALCSIZE , WM_NCHITTEST etc. removed for brevity):

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

Within OnPaintNonClientArea , I did:

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

Leaving OnPaintNonClientArea untouched, this removes the flicker:

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

So, why does DrawImageUnscaled cause this flickering? It seems to erase the area it works on with a white brush before drawing the buffer. I didn't find anything in the docs that clarified this issue. It wouldn't matter too much if it were just a small border around the control, but there will be text displayed within the NC area, so the area is clearly visible and therefore the flickering is really visible and annoying.

Related questions: Am I doing the native GDI stuff right, or are there potential problems I don't see right now? Also, when creating the ncBitmap , I'm using the control width and height, but GDI+ is resolution-independant, can there be any problems there?


To avoid flickering in UserControl's, I've had better luck with the BufferedGraphics class.

MSDN

Is that an option?

链接地址: http://www.djcxy.com/p/50304.html

上一篇: 玻璃上的渲染控件:找到解决方案,需要翻一番

下一篇: 为什么从WM中使用DrawImageUnscaled会导致闪烁