it-swarm.it

Come evitare lo sfarfallio in ListView quando si aggiorna un singolo testo ListViewItem?

Tutto quello che voglio è aggiornare un testo di ListViewItem senza vedere alcun sfarfallio.

Questo è il mio codice per l'aggiornamento (chiamato più volte):

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

Ho visto alcune soluzioni che riguardano l'override del 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);
}

Dicono che risolve il problema, ma nel mio caso non l'ha fatto . Credo che questo sia perché sto usando icone su ogni oggetto.

46
Jonas

Per terminare questa domanda, ecco una classe di supporto che deve essere chiamata quando il modulo viene caricato per ogni controllo ListView o qualsiasi altro controllo derivato di ListView nel modulo. Grazie a "Brian Gillespie" per aver dato la soluzione.

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

La risposta accettata funziona, ma è piuttosto lunga, e derivante dal controllo (come menzionato nelle altre risposte) solo per abilitare il doppio buffering è anche un po 'esagerata. Ma fortunatamente abbiamo una riflessione e possiamo anche chiamare i metodi interni se ci piace (ma sii sicuro di quello che fai!).

Incapsulando questo approccio in un metodo di estensione, otterremo una classe piuttosto breve:

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

Che può essere facilmente chiamato all'interno del nostro codice:

InitializeComponent();

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

E tutto lo sfarfallio è sparito.

Aggiornare

Ho inciampato su questa domanda ea causa di questo fatto, il metodo di estensione dovrebbe (forse) essere meglio:

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

Il ListView in CommonControls 6 (XP o successivi) supporta il doppio buffering. Fortunatamente, .NET avvolge i più recenti CommonControls sul sistema. Per abilitare il doppio buffering, inviare il messaggio di Windows appropriato al controllo ListView.

Ecco i dettagli: http://www.codeproject.com/KB/list/listviewxp.aspx

11
Brian Gillespie

In .NET Winforms 2.0 esiste una proprietà protetta denominata DoubleBuffered.

Tramite l'ereditarietà di ListView, è possibile impostare questa proprietà protetta su true. Ciò abiliterà il doppio buffering senza dover chiamare SendMessage.

L'impostazione della proprietà DoubleBuffered equivale all'impostazione del seguente stile:

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

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

10
Rolf Kristensen

So che questa domanda è abbastanza vecchia, ma poiché questo è uno dei primi risultati di ricerca su Google, volevo condividere la mia correzione.

L'unico modo per rimuovere lo sfarfallio del 100% era combinare la risposta di Oliver (classe di estensione con doppio buffering) e utilizzare i metodi BeignUpdate() e EndUpdate().

Nessuno di quelli da soli potrebbe risolvere lo sfarfallio per me . Concesso, io uso una lista molto complessa, che ho bisogno di inserire nella lista e anche bisogno di aggiornarlo quasi ogni secondo.

2
T4cC0re

questo aiuterà:

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

Se vuoi solo aggiornare il testo, imposta semplicemente il testo di SubItem modificato piuttosto che aggiornare l'intero ListViewItem (non hai detto come stai facendo gli aggiornamenti).

L'override che tu mostri equivale semplicemente a sovrascrivere OnPaintBackground, che sarebbe un modo "più corretto" per farlo, e non sarà di aiuto per un singolo oggetto.

Se hai ancora problemi, avremo bisogno di chiarimenti su ciò che hai effettivamente provato.

0
ctacke

Questo è uno sparo al buio, ma potresti provare a doppio buffering del controllo.

SetStyle(
  ControlStyles.AllPaintingInWmPaint |
  ControlStyles.UserPaint |
  ControlStyles.DoubleBuffer, true)
0
Ed S.

Chiamare il metodo BeginUpdate () su ListView prima di impostare uno degli elementi della vista elenco e quindi chiamare solo EndUpdate () dopo aver aggiunto tutti gli elementi.

Ciò fermerà lo sfarfallio.

0
Mike

La soluzione semplice è questa:

yourlistview.BeginUpdate ()

// Fai il tuo aggiornamento di aggiungere e rimuovere elementi dalla lista

yourlistview.EndUpdate ()

0
jaiveeru