¿Cómo evitar el parpadeo en ListView al actualizar el texto de un solo ListViewItem?

StackOverflow https://stackoverflow.com/questions/87795

  •  01-07-2019
  •  | 
  •  

Pregunta

Todo lo que quiero es actualizar el texto de un ListViewItem sin ver ningún parpadeo.

Este es mi código para actualizar (llamado varias veces):

listView.BeginUpdate();
listViewItem.SubItems[0].Text = state.ToString();    // update the state
listViewItem.SubItems[1].Text = progress.ToString(); // update the progress
listView.EndUpdate();

He visto algunas soluciones que implican anular el componente WndProc():

protected override void WndProc(ref Message m)
{
    if (m.Msg == (int)WM.WM_ERASEBKGND)
    {
        m.Msg = (int)IntPtr.Zero;
    }
    base.WndProc(ref m);
}

Dicen que soluciona el problema, pero en mi caso no fue así..Creo que esto se debe a que estoy usando íconos en cada elemento.

¿Fue útil?

Solución 2

Para finalizar esta pregunta, aquí hay una clase auxiliar que debe llamarse cuando se carga el formulario para cada ListView o cualquier otro control derivado de ListView en su formulario.Gracias a "Brian Gillespie" por dar la solución.

public enum ListViewExtendedStyles
{
    /// <summary>
    /// LVS_EX_GRIDLINES
    /// </summary>
    GridLines = 0x00000001,
    /// <summary>
    /// LVS_EX_SUBITEMIMAGES
    /// </summary>
    SubItemImages = 0x00000002,
    /// <summary>
    /// LVS_EX_CHECKBOXES
    /// </summary>
    CheckBoxes = 0x00000004,
    /// <summary>
    /// LVS_EX_TRACKSELECT
    /// </summary>
    TrackSelect = 0x00000008,
    /// <summary>
    /// LVS_EX_HEADERDRAGDROP
    /// </summary>
    HeaderDragDrop = 0x00000010,
    /// <summary>
    /// LVS_EX_FULLROWSELECT
    /// </summary>
    FullRowSelect = 0x00000020,
    /// <summary>
    /// LVS_EX_ONECLICKACTIVATE
    /// </summary>
    OneClickActivate = 0x00000040,
    /// <summary>
    /// LVS_EX_TWOCLICKACTIVATE
    /// </summary>
    TwoClickActivate = 0x00000080,
    /// <summary>
    /// LVS_EX_FLATSB
    /// </summary>
    FlatsB = 0x00000100,
    /// <summary>
    /// LVS_EX_REGIONAL
    /// </summary>
    Regional = 0x00000200,
    /// <summary>
    /// LVS_EX_INFOTIP
    /// </summary>
    InfoTip = 0x00000400,
    /// <summary>
    /// LVS_EX_UNDERLINEHOT
    /// </summary>
    UnderlineHot = 0x00000800,
    /// <summary>
    /// LVS_EX_UNDERLINECOLD
    /// </summary>
    UnderlineCold = 0x00001000,
    /// <summary>
    /// LVS_EX_MULTIWORKAREAS
    /// </summary>
    MultilWorkAreas = 0x00002000,
    /// <summary>
    /// LVS_EX_LABELTIP
    /// </summary>
    LabelTip = 0x00004000,
    /// <summary>
    /// LVS_EX_BORDERSELECT
    /// </summary>
    BorderSelect = 0x00008000,
    /// <summary>
    /// LVS_EX_DOUBLEBUFFER
    /// </summary>
    DoubleBuffer = 0x00010000,
    /// <summary>
    /// LVS_EX_HIDELABELS
    /// </summary>
    HideLabels = 0x00020000,
    /// <summary>
    /// LVS_EX_SINGLEROW
    /// </summary>
    SingleRow = 0x00040000,
    /// <summary>
    /// LVS_EX_SNAPTOGRID
    /// </summary>
    SnapToGrid = 0x00080000,
    /// <summary>
    /// LVS_EX_SIMPLESELECT
    /// </summary>
    SimpleSelect = 0x00100000
}

public enum ListViewMessages
{
    First = 0x1000,
    SetExtendedStyle = (First + 54),
    GetExtendedStyle = (First + 55),
}

/// <summary>
/// Contains helper methods to change extended styles on ListView, including enabling double buffering.
/// Based on Giovanni Montrone's article on <see cref="http://www.codeproject.com/KB/list/listviewxp.aspx"/>
/// </summary>
public class ListViewHelper
{
    private ListViewHelper()
    {
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern int SendMessage(IntPtr handle, int messg, int wparam, int lparam);

    public static void SetExtendedStyle(Control control, ListViewExtendedStyles exStyle)
    {
        ListViewExtendedStyles styles;
        styles = (ListViewExtendedStyles)SendMessage(control.Handle, (int)ListViewMessages.GetExtendedStyle, 0, 0);
        styles |= exStyle;
        SendMessage(control.Handle, (int)ListViewMessages.SetExtendedStyle, 0, (int)styles);
    }

    public static void EnableDoubleBuffer(Control control)
    {
        ListViewExtendedStyles styles;
        // read current style
        styles = (ListViewExtendedStyles)SendMessage(control.Handle, (int)ListViewMessages.GetExtendedStyle, 0, 0);
        // enable double buffer and border select
        styles |= ListViewExtendedStyles.DoubleBuffer | ListViewExtendedStyles.BorderSelect;
        // write new style
        SendMessage(control.Handle, (int)ListViewMessages.SetExtendedStyle, 0, (int)styles);
    }
    public static void DisableDoubleBuffer(Control control)
    {
        ListViewExtendedStyles styles;
        // read current style
        styles = (ListViewExtendedStyles)SendMessage(control.Handle, (int)ListViewMessages.GetExtendedStyle, 0, 0);
        // disable double buffer and border select
        styles -= styles & ListViewExtendedStyles.DoubleBuffer;
        styles -= styles & ListViewExtendedStyles.BorderSelect;
        // write new style
        SendMessage(control.Handle, (int)ListViewMessages.SetExtendedStyle, 0, (int)styles);
    }
}

Otros consejos

La respuesta aceptada funciona, pero es bastante larga, y derivar del control (como se menciona en las otras respuestas) solo para habilitar el doble almacenamiento en búfer también es un poco exagerado.Pero afortunadamente tenemos reflexión y también podemos llamar a métodos internos si queremos (¡pero asegúrate de lo que haces!).

Al encapsular este enfoque en un método de extensión, obtendremos una clase bastante corta:

public static class ControlExtensions
{
    public static void DoubleBuffering(this Control control, bool enable)
    {
        var method = typeof(Control).GetMethod("SetStyle", BindingFlags.Instance | BindingFlags.NonPublic);
        method.Invoke(control, new object[] { ControlStyles.OptimizedDoubleBuffer, enable });
    }
}

Que se puede llamar fácilmente dentro de nuestro código:

InitializeComponent();

myListView.DoubleBuffering(true); //after the InitializeComponent();

Y todo parpadeo desapareció.

Actualizar

me topé esta pregunta y debido a este hecho, el método de extensión debería (tal vez) ser mejor:

public static void DoubleBuffered(this Control control, bool enable)
{
    var doubleBufferPropertyInfo = control.GetType().GetProperty("DoubleBuffered", BindingFlags.Instance | BindingFlags.NonPublic);
    doubleBufferPropertyInfo.SetValue(control, enable, null);
}

ListView en CommonControls 6 (XP o posterior) admite doble almacenamiento en búfer.Afortunadamente, .NET incluye los CommonControls más nuevos del sistema.Para habilitar el doble almacenamiento en búfer, envíe el mensaje de Windows apropiado al control ListView.

Aquí están los detalles:http://www.codeproject.com/KB/list/listviewxp.aspx

En .NET Winforms 2.0 existe una propiedad protegida llamada DoubleBuffered.

Al heredar de ListView, se puede establecer esta propiedad protegida en verdadero.Esto permitirá el doble almacenamiento en búfer sin necesidad de llamar a SendMessage.

Configurar la propiedad DoubleBuffered es lo mismo que configurar el siguiente estilo:

listview.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.AllPaintingInWmPaint, true);

http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=94096

Sé que esta pregunta es bastante antigua, pero como es uno de los primeros resultados de búsqueda en Google, quería compartir mi solución.

La única manera de eliminar el parpadeo al 100% era combinar la respuesta de Oliver (clase de extensión con doble almacenamiento en búfer) y usar el BeignUpdate() y EndUpdate() métodos.

Ninguno de los dos por sí solo pudo solucionar el parpadeo por mí.Por supuesto, uso una lista muy compleja, que necesito incluir en la lista y también actualizarla casi cada segundo.

esto ayudará:

class DoubleBufferedListView : System.Windows.Forms.ListView
{
    public DoubleBufferedListView()
        :base()
    {
        this.DoubleBuffered = true;
    }
}

La solución simple es esta:

yourlistview.BeginUpdate()

//Haga su actualización para agregar y eliminar elementos de la lista

tuvistadelista.EndUpdate()

Si solo desea actualizar el texto, simplemente configure el texto del SubItem modificado directamente en lugar de actualizar el ListViewItem completo (no ha dicho cómo está haciendo las actualizaciones).

La anulación que muestra equivale a simplemente anular OnPaintBackground, lo que sería una forma administrada "más correcta" de realizar esa tarea, y no ayudará para un solo elemento.

Si aún tiene problemas, necesitaremos una aclaración sobre lo que realmente ha probado.

Este es un tiro en la oscuridad, pero podrías intentar hacer un doble buffer en el control.

SetStyle(
  ControlStyles.AllPaintingInWmPaint |
  ControlStyles.UserPaint |
  ControlStyles.DoubleBuffer, true)

Llame al método BeginUpdate() en ListView antes de configurar cualquiera de los elementos de la vista de lista y luego llame a EndUpdate() solo después de que se hayan agregado todos los elementos.

Eso detendrá el parpadeo.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top