web-dev-qa-db-fra.com

Comment empêcher le scintillement dans ListView lors de la mise à jour d'un texte ListViewItem unique?

Tout ce que je veux, c'est mettre à jour le texte d'un ListViewItem sans voir de scintillement.

Voici mon code de mise à jour (appelé plusieurs fois):

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

J'ai vu des solutions qui impliquent de surcharger la fonction WndProc(): du composant

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

Ils disent que cela résout le problème, mais dans mon cas, il ne l'a pas fait . Je pense que c’est parce que j’utilise des icônes pour chaque élément.

46
Jonas

Pour terminer cette question, voici une classe d'assistance qui doit être appelée lors du chargement du formulaire pour chaque contrôle ListView ou tout autre contrôle dérivé de ListView dans votre formulaire. Merci à "Brian Gillespie" pour avoir donné la solution.

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 réponse acceptée fonctionne, mais est assez longue, et dériver du contrôle (comme mentionné dans les autres réponses) juste pour activer la double mise en mémoire tampon est aussi un peu exagéré. Mais heureusement, nous avons de la réflexion et pouvons aussi faire appel à des méthodes internes si nous le souhaitons (mais soyez sûr de ce que vous faites!).

Encapsulant cette approche dans une méthode d'extension, nous aurons une classe assez courte:

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

Ce qui peut facilement être appelé dans notre code:

InitializeComponent();

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

Et tout le scintillement est parti.

Mettre à jour

Je suis tombé sur cette question et de ce fait, la méthode d'extension devrait (peut-être) être:

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

Le ListView dans CommonControls 6 (XP ou plus récent) prend en charge le double buffering. Heureusement, .NET encapsule les derniers CommonControls sur le système. Pour activer la double mise en mémoire tampon, envoyez le message Windows approprié au contrôle ListView.

Voici les détails: http://www.codeproject.com/KB/list/listviewxp.aspx

11
Brian Gillespie

Dans .NET Winforms 2.0, il existe une propriété protégée appelée DoubleBuffered.

En héritant de ListView, vous pouvez définir cette propriété protégée sur true. Cela activera la double mise en mémoire tampon sans qu'il soit nécessaire d'appeler SendMessage.

La définition de la propriété DoubleBuffered est identique à la définition du style suivant:

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

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

10
Rolf Kristensen

Je sais que cette question est assez ancienne, mais parce que c’est l’un des premiers résultats de recherche sur Google, je voulais partager ma solution.

La seule façon de supprimer le scintillement à 100% était de combiner la réponse d'Oliver (classe d'extension avec double tampon) et d'utiliser les méthodes BeignUpdate() et EndUpdate().

Aucune de ces solutions ne pourrait résoudre le scintillement pour moi… __. D'accord, j'utilise une liste très complexe, que je dois intégrer à la liste et la mettre à jour presque toutes les secondes.

2
T4cC0re

cA aidera:

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

Si vous souhaitez uniquement mettre à jour le texte, définissez simplement le texte du sous-élément modifié directement plutôt que de mettre à jour l'intégralité de ListViewItem (vous n'avez pas expliqué comment vous réalisiez vos mises à jour).

La substitution que vous affichez équivaut à remplacer simplement OnPaintBackground, ce qui constituerait une méthode gérée "plus correcte" pour effectuer cette tâche, qui ne servira à rien pour un seul élément.

Si vous avez toujours des problèmes, nous aurons besoin d'éclaircissements sur ce que vous avez réellement essayé.

0
ctacke

C'est un coup dans le noir, mais vous pouvez essayer de mettre en double tampon le contrôle.

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

Appelez la méthode BeginUpdate () sur ListView avant de définir l'un des éléments de la vue liste, puis appelez uniquement EndUpdate () une fois tous les éléments ajoutés.

Cela arrêtera le scintillement.

0
Mike

La solution simple est la suivante:

yourlistview.BeginUpdate ()

// Faites votre mise à jour pour ajouter et supprimer des éléments de la liste

yourlistview.EndUpdate ()

0
jaiveeru