2010-04-10 46 views
87

En mi aplicación, me estoy moviendo constantemente de un control a otro. Yo he creado no. de controles de usuario, pero durante la navegación mis controles parpadean. lleva 1 o 2 segundos para actualizar. Intenté configurar estoCómo corregir el parpadeo en Controles de usuario

SetStyle(ControlStyles.OptimizedDoubleBuffer, true); 
or 
SetStyle(ControlStyles.UserPaint, true); 
SetStyle(ControlStyles.AllPaintingInWmPaint, true); 
SetStyle(ControlStyles.DoubleBuffer, true); 

pero no sirvió ... Cada control tiene la misma imagen de fondo con diferentes controles. Entonces, ¿cuál es la solución para ello ...
Gracias.

+0

¿Dónde están estas declaraciones? Idealmente, póngalos en el constructor. ¿Llamaste a 'UpdateStyles' después de configurar esto? Está mal documentado, pero a veces puede ser necesario. – Thomas

Respuesta

260

No es el tipo de parpadeo que puede resolver el doble buffer. Ni BeginUpdate o SuspendLayout. Tienes demasiados controles, la imagen de fondo puede empeorar un lote.

Se inicia cuando el UserControl se pinta a sí mismo. Dibuja la imagen de fondo, dejando agujeros donde van las ventanas de control infantil. Cada control secundario recibe un mensaje para pintarse a sí mismo, llenarán el agujero con su contenido de ventana. Cuando tienes muchos controles, esos agujeros son visibles para el usuario por un tiempo. Normalmente son blancos, que contrastan mal con la imagen de fondo cuando está oscuro. O pueden ser negras si el formulario tiene su propiedad Opacidad o Transparencia clave establecida, lo que contrasta mal con casi cualquier cosa.

Esta es una limitación bastante fundamental de Windows Forms, está atascada con la forma en que Windows procesa Windows. Reparado por WPF por cierto, no usa Windows para controles secundarios. Lo que desearía es almacenar dos veces en el formulario completo, incluidos los controles secundarios. Es posible, verifique mi código en this thread para la solución. Sin embargo, tiene efectos secundarios y en realidad no aumenta la velocidad de la pintura. El código es simple, pegue esto en su forma (no el control de usuario):

protected override CreateParams CreateParams { 
    get { 
    CreateParams cp = base.CreateParams; 
    cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED 
    return cp; 
    } 
} 

Hay muchas cosas que puede hacer para mejorar la velocidad de la pintura, hasta el punto de que el parpadeo no se nota más. Comience abordando la imagen de fondo. Pueden ser realmente caros cuando la imagen de origen es grande y debe reducirse para ajustarse al control. Cambie la propiedad BackgroundImageLayout a "Mosaico". Si eso le da una notable aceleración, regrese a su programa de pintura y cambie el tamaño de la imagen para que concuerde mejor con el tamaño de control típico.O escriba el código en el método OnResize() de la UC para crear una copia del tamaño adecuado de la imagen para que no tenga que cambiar su tamaño cada vez que se repinte el control. Use el formato de píxeles Format32bppPArgb para esa copia, que se muestra aproximadamente 10 veces más rápido que cualquier otro formato de píxeles.

Lo siguiente que puede hacer es evitar que los agujeros sean tan perceptibles y que contrasten mal con la imagen. Puede turn off el indicador de estilo WS_CLIPCHILDREN para el UC, el indicador que impide que el UC se dibuje en el área donde van los controles secundarios. Pegue este código en el código del UserControl:

protected override CreateParams CreateParams { 
    get { 
    var parms = base.CreateParams; 
    parms.Style &= ~0x02000000; // Turn off WS_CLIPCHILDREN 
    return parms; 
    } 
} 

Los controles secundarios ahora se pintarán encima de la imagen de fondo. Es posible que todavía los veas pintar uno por uno, pero el feo agujero blanco o negro intermedio no será visible.

Por último, reducir el número de controles secundarios siempre es un buen enfoque para resolver problemas lentos de pintura. Anule el evento OnPaint() de la UC y dibuje lo que ahora se muestra en un niño. Particular Label y PictureBox son muy desperdicio. Conveniente para apuntar y hacer clic, pero su alternativa ligera (dibujar una cadena o una imagen) toma solo una línea de código en su método OnPaint().

3

Si usted está haciendo cualquier pintura personalizada en el control (es decir primordial OnPaint), puede intentar el doble buffering mismo.

Image image; 
protected override OnPaint(...) { 
    if (image == null || needRepaint) { 
     image = new Bitmap(Width, Height); 
     using (Graphics g = Graphics.FromImage(image)) { 
      // do any painting in image instead of control 
     } 
     needRepaint = false; 
    } 
    e.Graphics.DrawImage(image, 0, 0); 
} 

e invalidar su control con una propiedad NeedRepaint

De lo contrario la respuesta anterior con SuspendLayout y ResumeLayout es probablemente lo que quiere.

+0

Este es un método creativo para simular doublebuffer !. Puede agregar 'if (image! = Null) image.Dispose();' before 'image = new Bitmap ...' –

-2

¿Has probado Control.DoubleBuffered propiedad?

Obtiene o establece un valor que indica si este control debe volver a dibujar su superficie con un búfer secundario para reducir o evitar el parpadeo.

También this y this pueden ayudar.

-9

No hay necesidad de cualquier Doblebuffering y todo lo que los chicos cosas ...

una solución simple ...

Si está utilizando la interfaz MDI, simplemente pega el siguiente código en el forma principal. Eliminará todo parpadeo de las páginas. Sin embargo, algunas páginas que requieren más tiempo para cargar aparecerán en 1 o 2 segundos. Pero esto es mejor que mostrar una página parpadeante en la que cada elemento viene uno por uno.

Esta es la única mejor solución para toda la aplicación. Ver el código para poner en la forma principal:

protected override CreateParams CreateParams { 
    get { 
    CreateParams cp = base.CreateParams; 
    cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED 
    return cp; 
    } 
} 
+11

Entonces, lo que está diciendo es que la respuesta que Hans proporcionó hace más de dos años es, de hecho, ¿correcto? Gracias, Kshitiz. ¡Eso es muy útil! – Fernando

0

sólo para añadir a la respuesta Hans dio:

(versión TLDR: La transparencia es más pesado de lo que piensa, utiliza solamente colores sólidos en todas partes)

Si WS_EX_COMPOSITED, DoubleBuffered y WS_CLIPCHILDREN no resolvieron tu parpadeo (para mí WS_CLIPCHILDREN lo empeoró aún más), prueba esto: revisa TODOS tus controles y todo tu código, y donde sea que tengas Cualquier transparencia o semitransparencia para BackColor, ForeColor, o cualquier otro color, simplemente quítelo, use solo colores sólidos. En la mayoría de los casos en los que cree que solo tiene para utilizar la transparencia, no es así. Vuelva a diseñar su código y controles, y use colores sólidos. Tuve un terrible y terrible parpadeo y el programa se estaba volviendo lento. Una vez que eliminé la transparencia, se aceleró significativamente, y hay 0 parpadeo.

EDITAR: Para agregar más, acabo de descubrir que WS_EX_COMPUESTO no tiene que ser en toda la ventana, ¡podría aplicarse solo a controles específicos! Esto me ahorró muchos problemas. Simplemente haga un control personalizado heredado de cualquier control que necesite, y pegue la anulación ya publicada para WS_EX_COMPOSITED. ¡De esta forma obtendrás doble buffer de bajo nivel solo en este control, evitando los desagradables efectos secundarios en el resto de la aplicación!

7

Esto es un problema real, y la respuesta que dio Hans Passant es excelente para guardar el parpadeo. Sin embargo, hay efectos secundarios como él mencionó, y pueden ser feos (IU feo). Como se indicó, "Puede desactivar el indicador de estilo WS_CLIPCHILDREN para la UC", pero eso solo lo desactiva para una UC.Los componentes en el formulario principal todavía tienen problemas.

Por ejemplo, una barra de desplazamiento del panel no se pinta, porque técnicamente está en el área secundaria. Sin embargo, el componente secundario no dibuja la barra de desplazamiento, por lo que no se pintará hasta que el mouse pase (u otro evento lo active).

Además, los iconos animados (cambiar iconos en un bucle de espera) no funcionan. Eliminar iconos en una tabPage.ImageKey no redimensiona/vuelve a pintar las otras TabPages de forma adecuada.

Así que estaba buscando una manera de apagar WS_CLIPCHILDREN en la pintura inicial para que mi Formulario se cargue bien pintado, o mejor aún solo enciéndalo mientras se cambia el tamaño de mi formulario con una gran cantidad de componentes.

El truco es hacer que la aplicación llame a CreateParams con el estilo deseado WS_EX_COMPOSITED/WS_CLIPCHILDREN? Encontré un truco aquí (http://www.angryhacker.com/blog/archive/2010/07/21/how-to-get-rid-of-flicker-on-windows-forms-applications.aspx) y funciona muy bien. Gracias AngryHacker!

Pongo la llamada TurnOnFormLevelDoubleBuffering() en el evento ResizeBegin de formulario. TurnOffFormLevelDoubleBuffering() en el evento de formulario ResizeEnd (o simplemente dejarlo WS_CLIPCHILDREN después de que se pintó inicialmente correctamente.)

int originalExStyle = -1; 
    bool enableFormLevelDoubleBuffering = true; 

    protected override CreateParams CreateParams 
    { 
     get 
     { 
      if (originalExStyle == -1) 
       originalExStyle = base.CreateParams.ExStyle; 

      CreateParams cp = base.CreateParams; 
      if (enableFormLevelDoubleBuffering) 
       cp.ExStyle |= 0x02000000; // WS_EX_COMPOSITED 
      else 
       cp.ExStyle = originalExStyle; 

      return cp; 
     } 
    } 

    public void TurnOffFormLevelDoubleBuffering() 
    { 
     enableFormLevelDoubleBuffering = false; 
     this.MaximizeBox = true; 
    } 
+0

Su código no incluye el método TurnOnFormLevelDoubleBuffering() ... –

+0

@DanW Eche un vistazo a la URL publicada en esta respuesta (http://www.angryhacker.com/blog/archive/2010/07/21/how -to-get-rid-of-flicker-on-windows-forms-applications.aspx) – ChrisB

1

En el formulario principal o control de usuario, donde la imagen de fondo reside establecer la propiedad BackgroundImageLayout a Center o Stretch . Notará una gran diferencia cuando se esté procesando el control del usuario.

0

Sé que esta pregunta es muy antigua, pero quiero dar mi experiencia al respecto.

Tuve un montón de problemas con Tabcontrol parpadeando en un formulario con OnPaint y/o OnPaintBackGround en Windows 8 usando .NET 4.0.

La única piensan que ha sido trabajado NO USAR el método Graphics.DrawImage en OnPaint anulaciones, en otras palabras, cuando sorteo se realiza directamente a los gráficos proporcionados por el PaintEventArgs, incluso la pintura de todo el rectángulo, el parpadeo desapareció. Pero si llama al método DrawImage, incluso dibujando un mapa de bits recortado, (creado para doble almacenamiento en búfer) aparece el parpadeo.

Espero que ayude!

0

I combinado this flicker fix y this font fix, entonces tuve que añadir un poco de mi propio código para iniciar un temporizador en la pintura para invalidar la TabControl cuando se va fuera de la pantalla y la espalda, etc ..

Los tres hacen esto:

using System; 
using System.Runtime.InteropServices; 
using System.Windows.Forms; 
public class TabControlEx:TabControl 
{ 
    [DllImport("user32.dll")] 
    private static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam); 
    private const int WM_PAINT = 0x0f; 
    private const int WM_SETFONT = 0x30; 
    private const int WM_FONTCHANGE = 0x1d; 
    private System.Drawing.Bitmap buffer; 
    private Timer timer = new Timer(); 
    public TabControlEx() 
    { 
     timer.Interval = 1; 
     timer.Tick += timer_Tick; 
     this.SetStyle(ControlStyles.UserPaint | ControlStyles.DoubleBuffer | ControlStyles.AllPaintingInWmPaint, true); 
    } 
    void timer_Tick(object sender, EventArgs e) 
    { 
     this.Invalidate(); 
     this.Update(); 
     timer.Stop(); 
    } 
    protected override void WndProc(ref Message m) 
    { 
     if (m.Msg == WM_PAINT) timer.Start(); 
     base.WndProc(ref m); 
    } 
    protected override void OnPaint(PaintEventArgs pevent) 
    { 
     this.SetStyle(ControlStyles.UserPaint, false); 
     base.OnPaint(pevent); 
     System.Drawing.Rectangle o = pevent.ClipRectangle; 
     System.Drawing.Graphics.FromImage(buffer).Clear(System.Drawing.SystemColors.Control); 
     if (o.Width > 0 && o.Height > 0) 
     DrawToBitmap(buffer, new System.Drawing.Rectangle(0, 0, Width, o.Height)); 
     pevent.Graphics.DrawImageUnscaled(buffer, 0, 0); 
     this.SetStyle(ControlStyles.UserPaint, true); 
    } 

    protected override void OnResize(EventArgs e) 
    { 
     base.OnResize(e); 
     buffer = new System.Drawing.Bitmap(Width, Height); 
    } 
    protected override void OnCreateControl() 
    { 
     base.OnCreateControl(); 
     this.OnFontChanged(EventArgs.Empty); 
    } 
    protected override void OnFontChanged(EventArgs e) 
    { 
     base.OnFontChanged(e); 
     IntPtr hFont = this.Font.ToHfont(); 
     SendMessage(this.Handle, WM_SETFONT, hFont, (IntPtr)(-1)); 
     SendMessage(this.Handle, WM_FONTCHANGE, IntPtr.Zero, IntPtr.Zero); 
     this.UpdateStyles(); 
    } 
} 

que no soy el creador, pero por lo que entiendo el mapa de bits hace todo el bypass de errores.

Esto fue lo único que definitivamente resolvió TabControl (con Iconos) parpadeando para mí.

diferencia consecuencia de vídeo: vainilla tabcontrol vs tabcontrolex

http://gfycat.com/FineGlitteringDeermouse

ps.Necesitará establecer HotTrack = true, porque esto corrige ese error también

1

Intenté agregar esto como un comentario pero no tengo suficientes puntos. Esto es lo único que alguna vez ayudó a mis problemas de parpadeo, muchas gracias a Hans por su publicación. Para cualquiera que esté utilizando el generador de C++ como yo, aquí está la traducción

Agregue la declaración CreateParams al archivo .h de la forma principal de la aplicación, p. Ej.

class TYourMainFrom : public TForm 
{ 
protected: 
    virtual void __fastcall CreateParams(TCreateParams &Params); 
} 

y añadir esto a su archivo .cpp

void __fastcall TYourMainForm::CreateParams(TCreateParams &Params) 
{ 
    Params.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED 
    TForm::CreateParams(Params); 
} 
1

Reflexión hace el truco! Lanza esto en tu constructor o evento OnLoad y si usas algunos UserControls personalizados que tienen subcontroles, necesitarás asegurarte de que esos controles personalizados con los subcontroles también tengan esto.

NOTA: No use esto en algunos eventos como OnPaint que se ejecutan trillones de veces por segundo porque la reflexión es bastante costosa de usar.

Explicación: El Control/Form padre iterará a través de todos los controles en el formulario y establecerá su propiedad en DoubleBuffered.

Código:

foreach (Control control in Controls) // reflection to sort flickering. 
    { 
     typeof(Control).InvokeMember("DoubleBuffered", 
      BindingFlags.SetProperty | BindingFlags.Instance | BindingFlags.NonPublic, 
      null, control, new object[] { true }); 
    } 
Cuestiones relacionadas