2009-01-14 32 views
44

Tengo una vista de lista que se actualiza periódicamente (cada 60 segundos). Me molestaba que tuviera un destello cada vez que salía. El método utilizado fue borrar todos los elementos y luego recrearlos. Decidí que, en lugar de borrar los artículos, simplemente escribiría directamente en la celda con el nuevo texto. ¿Es este un mejor enfoque o alguien tiene una mejor solución?C# parpadeo Listview en la actualización

Respuesta

77

El control ListView tiene un problema de parpadeo. El problema parece ser que la sobrecarga de actualización del control está implementada de manera incorrecta, por lo que actúa como un Refresco. Una actualización debe hacer que el control vuelva a dibujar solo las regiones no válidas, mientras que una actualización vuelve a dibujar el área de cliente completa del control. Entonces, si tuviera que cambiar, por ejemplo, el color de fondo de un elemento en la lista, entonces solo ese elemento en particular debería ser repintado. Desafortunadamente, el control ListView parece ser de una opinión diferente y quiere volver a pintar toda su superficie cada vez que se mete con un solo elemento ... incluso si el elemento no se muestra actualmente. Así que, de todos modos, puede suprimir fácilmente el parpadeo haciendo rodar su propio de la siguiente manera:

class ListViewNF : System.Windows.Forms.ListView 
{ 
    public ListViewNF() 
    { 
     //Activate double buffering 
     this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true); 

     //Enable the OnNotifyMessage event so we get a chance to filter out 
     // Windows messages before they get to the form's WndProc 
     this.SetStyle(ControlStyles.EnableNotifyMessage, true); 
    } 

    protected override void OnNotifyMessage(Message m) 
    { 
     //Filter out the WM_ERASEBKGND message 
     if(m.Msg != 0x14) 
     { 
      base.OnNotifyMessage(m); 
     } 
    } 
} 

Desde: Geekswithblogs.net

+1

Debe saber que la desventaja de esta solución (debido al doble almacenamiento en búfer) es que la actualización del control es significativamente más lenta (intente agregar 1000 elementos). –

+1

Para ser sincero, nunca utilizo la lista de lista de winforms. Pero, ¿no está perdiendo ese rendimiento cuando llama a ** SuspendLayout ** y ** ResumeLayout **? – Stormenet

+0

Gracias Stormenent. He estado buscando una solución a este problema por un tiempo y esto funciona bien para mí. –

4

Sí, lo convierten en doble buffer. Reducirá el parpadeo;) http://msdn.microsoft.com/en-us/library/system.windows.forms.listview.doublebuffered.aspx

+1

-1: No puede establecer una propiedad DoubleBuffered 'ListView's (está protegida). –

+1

-1 a su comentario porque PUEDE configurarlo, si obtiene una nueva clase de él, también existe el método "SetStyle". Pero forma de hacer su investigación antes de votar a alguien, incluso cuando respondió usando la razón exacta por la que su comentario no tiene sentido * pulgares arriba * – SilverX

+0

solo recuerde, SetStyle también está protegido ... ¿cómo logró llamar eso? – SilverX

0

Intente configurar la propiedad del doble búfer en verdadero.

También puede usar:

this.SuspendLayout(); 

//update control 

this.ResumeLayout(False); 

this.PerformLayout(); 
+0

El diseño se aplica principalmente al mover/cambiar el tamaño de las cosas ... Sospecho que te refieres a [Begin | End] Update, según mi respuesta. –

21

Además de las otras respuestas, muchos controles tienen un método [Begin|End]Update() que se puede utilizar para reducir el parpadeo cuando se edita el contenido - por ejemplo:

listView.BeginUpdate(); 
    try { 
     // listView.Items... (lots of editing) 
    } finally { 
     listView.EndUpdate(); 
    } 
+0

+1: Gracias; esto hizo una diferencia notable :-) –

+0

Enorme agradecimiento! He estado buscando en Internet hacia arriba y hacia abajo, y ninguno de los resultados de búsqueda es aplicable: D – pepoluan

+1

Encontré que esto ayuda, pero no elimina por completo el parpadeo. – usr

4

Excelente pregunta y la respuesta de Stormenent fue perfecta. Aquí hay un puerto C++ de su código para cualquier otra persona que pueda estar abordando implementaciones de C++/CLI.

#pragma once 

#include "Windows.h" // For WM_ERASEBKGND 

using namespace System; 
using namespace System::Windows::Forms; 
using namespace System::Data; 
using namespace System::Drawing; 

public ref class FlickerFreeListView : public ListView 
{ 
public: 
    FlickerFreeListView() 
    { 
     //Activate double buffering 
     SetStyle(ControlStyles::OptimizedDoubleBuffer | ControlStyles::AllPaintingInWmPaint, true); 

     //Enable the OnNotifyMessage event so we get a chance to filter out 
     // Windows messages before they get to the form's WndProc 
     SetStyle(ControlStyles::EnableNotifyMessage, true); 
    } 

protected: 
    virtual void OnNotifyMessage(Message m) override 
    { 
     //Filter out the WM_ERASEBKGND message 
     if(m.Msg != WM_ERASEBKGND) 
     { 
      ListView::OnNotifyMessage(m); 
     } 
    } 

}; 
3

Si esto puede ayudar, el siguiente componente resuelto mis problemas de parpadeo ListView con .NET 3,5

[ToolboxItem(true)] 
[ToolboxBitmap(typeof(ListView))] 
public class ListViewDoubleBuffered : ListView 
{ 
    public ListViewDoubleBuffered() 
    { 
     this.DoubleBuffered = true; 
    } 
} 

lo uso en conjonction con .BeginUpdate() y .EndUpdate() métodos donde hago ListView.Items manipulación.

No entiendo por qué esta propiedad es un protegido uno ... incluso en .NET 4.5 (tal vez un problema de seguridad)

1

solución simple

yourlistview.BeginUpdate() 

//Do your update of adding and removing item from the list 

yourlistview.EndUpdate() 
+0

Esto funcionó para mí cuando tenía una vista de lista que tenía limitada a 100 elementos, y estaba borrando la primera fila cuando añadía una nueva fila si se cumplía ese umbral. –

3

La solución más simple sería probablemente usando

 listView.Items.AddRange(listViewItems.ToArray()); 

en lugar de

 foreach (ListViewItem listViewItem in listViewItems) 
     { 
      listView.Items.Add(listViewItem); 
     } 

Esto funciona mucho mejor.

+0

Muchas gracias tuve un problema real con el bloqueo de mi GUI completa con solo agregar elementos, esto lo resolvió. –

0

Sé que es extremadamente anterior pregunta y respuesta. Sin embargo, este es el resultado principal al buscar "C++/cli listview flicker", a pesar de que ni siquiera se trata de C++.Así que aquí está la versión de C++ de esta:

pongo esto en el archivo de cabecera para mi forma principal, se puede optar por poner en otro lugar ...

static void DoubleBuffer(Control^ control, bool enable) { 
    System::Reflection::PropertyInfo^ info = control->GetType()-> 
     GetProperty("DoubleBuffered", System::Reflection::BindingFlags::Instance 
      | System::Reflection::BindingFlags::NonPublic); 
    info->SetValue(control, enable, nullptr); 
} 

Si le sucede a la tierra aquí en busca de una respuesta similar para C++ administrado, eso funciona para mí. :)

0

En Winrt Windows phone 8.1 puede establecer el siguiente código para solucionar este problema.

<ListView.ItemContainerTransitions> 
    <TransitionCollection/>  
</ListView.ItemContainerTransitions> 
1

Aquí está mi solución rápida para una aplicación C# que no requiere la subclasificación de las vistas de lista, etc.

utiliza la reflexión para establecer la propiedad DoubleBuffered a tratar en el constructor de formas.

lvMessages 
     .GetType() 
     .GetProperty("DoubleBuffered", System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.NonPublic) 
     .SetValue(lvMessages, true, null); 
Cuestiones relacionadas