2011-06-06 21 views
5

Actualmente estoy construyendo un control derivado de System.Windows.Forms.ContainerControl que tiene un área de borde que necesito pintar yo mismo. Como no hay OnPaintNonClientArea para anular, he construido yo mismo como esto (manejo de otros mensajes como WM_NCCALCSIZE, WM_NCHITTEST etc. eliminado por razones de brevedad):¿Por qué DrawImageUnscaled causa parpadeo cuando se usa desde WM_NCPAINT?

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

Dentro OnPaintNonClientArea, lo hice:

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

Dejando OnPaintNonClientArea sin tocar, lo que elimina el parpadeo:

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

Así que, ¿por qué DrawImageUnscaled causa este parpadeo? Parece borrar el área donde trabaja con un pincel blanco antes de dibujar el búfer. No encontré nada en los documentos que aclaró este problema. No importaría demasiado si fuera solo un pequeño borde alrededor del control, pero se mostrará texto dentro del área NC, por lo que el área es claramente visible y, por lo tanto, el parpadeo es realmente visible y molesto.

Preguntas relacionadas: ¿Estoy haciendo las cosas nativas de GDI, o hay problemas potenciales que no veo ahora? Además, al crear el ncBitmap, estoy usando el control de ancho y alto, pero GDI + es independiente de la resolución, ¿puede haber algún problema allí?

+0

¿Ha intentado utilizar Double Buffering en el formulario? Se supone que Double Buffering se ocupa de problemas de gráficos como este. Además, ¿hay equivalentes SuspendLayout y PerformLayout que podría utilizar para evitar que el control se actualice antes de que haya cargado el objeto gráfico? –

+0

Gracias por esto. Estuve jugando con esto durante bastante tiempo y probé todo tipo de cosas, en un momento hice doble buffer de forma que no hizo la diferencia. No creo que esto se aplique aquí, ya que efectivamente hago doblebuffering ya - estoy pintando todas las cosas usando un objeto 'Graphics' y luego usando' DrawImageUnscaled' Intento pintar el buffer en el objeto Graphics creado por ' Graphics.FromHdc (hDC) '. Se trata de DrawImageUnscaled que primero borra el área que se dibujará con un pincel blanco y luego dibuja el contenido del objeto Graphics. No estoy seguro de si esto puede ser reparado. – takrl

Respuesta

2

Para evitar el parpadeo en UserControl, he tenido mejor suerte con la clase BufferedGraphics.

MSDN

¿Eso es una opción?

+1

+1 por un enfoque que desconocía. Pero para pintar el área no cliente, esto causó tanto parpadeo como el primer enfoque que probé. – takrl

Cuestiones relacionadas