c# - Cómo arreglar el parpadeo en los controles de usuario
c# doublebuffered (12)
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. Traté de configurar esto
SetStyle(ControlStyles.OptimizedDoubleBuffer, true);
or
SetStyle(ControlStyles.UserPaint, true);
SetStyle(ControlStyles.AllPaintingInWmPaint, true);
SetStyle(ControlStyles.DoubleBuffer, true);
pero no ayudó ... Cada control tiene la misma imagen de fondo con diferentes controles. Entonces, ¿cuál es la solución para ello ...
Gracias.
¿ Control.DoubleBuffered
propiedad Control.DoubleBuffered
?
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.
Combiné este parche de parpadeo y esta corrección de fuente , luego tuve que agregar un poco de mi propio código para iniciar un temporizador en pintura para invalidar TabControl cuando sale de la pantalla y viceversa, 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();
}
}
No soy el creador, pero por lo que entiendo, el mapa de bits no pasa por alto ningún error.
Esto fue lo único que resolvió definitivamente TabControl (con Íconos) parpadeando para mí.
video de diferencia de resultado: tabcontrol vs tabcontrolex
http://gfycat.com/FineGlitteringDeermouse
PD. Necesitarás establecer HotTrack = true, porque esto corrige ese error también
En el formulario principal o control de usuario donde reside la imagen de fondo, establezca la propiedad BackgroundImageLayout
en Center
o Stretch
. Notará una gran diferencia cuando se esté procesando el control del usuario.
Este 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 pinta, porque técnicamente se encuentra 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 forma de desactivar WS_CLIPCHILDREN en la pintura inicial para que mi Formulario se cargue bien pintado, o mejor aún, enciéndalo mientras se cambia el tamaño de mi formulario con muchos 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 genial. Gracias AngryHacker!
Puse la llamada TurnOnFormLevelDoubleBuffering () en el evento ResizeBegin de formulario. Llamada TurnOffFormLevelDoubleBuffering () en el evento ResizeEnd de formulario (o simplemente déjelo 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;
}
La 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 / formulario padre repetirá 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 });
}
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 las cosas.
Comienza cuando 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, revisa mi código en este hilo 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 formulario (no en el control del 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 ya no se nota. 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 apagar 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 de 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, pero no menos importante, 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 derrochadores. 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 ().
No hay necesidad de ningún búfer doble y todas esas cosas chicos ...
Una solución simple ...
Si está utilizando la interfaz MDI, simplemente pegue el siguiente código en el formulario 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. Vea el código para poner en el formulario principal:
protected override CreateParams CreateParams {
get {
CreateParams cp = base.CreateParams;
cp.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
return cp;
}
}
Pruebe los métodos BeginUpdate / EndUpdate O SuspendLayout / ResumeLayout. Ver siguiente
Cómo solucionar problemas de parpadeo de control winform anidados
Parpadeo durante las actualizaciones de controles en WinForms (por ejemplo, DataGridView)
Sé que esta pregunta es muy antigua, pero quiero dar mi experiencia al respecto.
Tuve muchos problemas con el parpadeo de Tabcontrol
en un formulario con OnPaint
y / o OnPaintBackGround
en Windows 8 usando .NET 4.0.
El único pensamiento que funcionó NO ha sido OnPaint
método Graphics.DrawImage
en OnPaint
anula, en otras palabras, cuando se dibuja directamente en los gráficos proporcionados por PaintEventArgs
, incluso pintando todo el rectángulo, el parpadeo desapareció. Pero si se llama al método DrawImage
, incluso dibujando un mapa de bits recortado, (creado para el doble almacenamiento en búfer) aparece el parpadeo.
¡Espero eso ayude!
Si está haciendo una pintura personalizada en el control (es decir, anulando OnPaint), puede probar el doble buffer usted 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 desea.
Solo para agregar a la respuesta que dio Hans:
(Versión TLDR: la transparencia es más pesada de lo que piensas, utiliza solo 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 que usar 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 para toda la ventana, ¡se podría aplicar 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!
Traté de 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é usando el generador de C ++ como yo, aquí está la traducción
Agregue la declaración CreateParams al archivo .h de la forma principal de su aplicación, por ejemplo
class TYourMainFrom : public TForm
{
protected:
virtual void __fastcall CreateParams(TCreateParams &Params);
}
y agregue esto a su archivo .cpp
void __fastcall TYourMainForm::CreateParams(TCreateParams &Params)
{
Params.ExStyle |= 0x02000000; // Turn on WS_EX_COMPOSITED
TForm::CreateParams(Params);
}