Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does DrawImageUnscaled cause flickering when used from WM_NCPAINT?

Tags:

c#

gdi+

gdi

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?

like image 411
takrl Avatar asked Nov 04 '22 20:11

takrl


1 Answers

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

MSDN

Is that an option?

like image 163
John66NY Avatar answered Nov 15 '22 09:11

John66NY