Como evitar a cintilação em ListView ao atualizar um único texto do ListViewItem?

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

  •  01-07-2019
  •  | 
  •  

Pergunta

Tudo que eu quero é para atualizar texto de um ListViewItem whithout ver qualquer cintilação.

Este é meu código para atualização (chamado várias vezes):

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

Eu vi algumas soluções que envolvem substituindo WndProc(): do componente

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

Eles dizem que resolve o problema, mas no meu caso não . Eu acredito que este é porque eu estou usando ícones em cada item.

Foi útil?

Solução 2

Para terminar esta pergunta, aqui está uma classe de ajuda que deve ser chamado quando o formulário é de carregamento de cada ListView ou controle derivado de qualquer outro ListView no formulário. Graças ao "Brian Gillespie" para dar a solução.

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

Outras dicas

A resposta aceita funciona, mas é bastante longa, e derivando do controle (como mencionado em outras respostas) apenas para permitir que o buffer duplo também é um pouco exagerado. Mas, felizmente, temos a reflexão e também pode chamar métodos internos se como a (mas não se esqueça que você faz!).

Seja encapsular esta abordagem em um método de extensão, que vai ter um bastante curto classe:

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 pode ser facilmente chamado de dentro de nosso código:

InitializeComponent();

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

E tudo cintilação está desaparecido.

Atualização

Eu tropecei em esta questão e devido a este fato, o método de extensão deve (talvez) melhor que seja:

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

O ListView em CommonControls 6 (XP ou mais recente) suporta double buffering. Felizmente, .NET envolve os mais novos CommonControls no sistema. Para ativar o buffer duplo, enviar a mensagem Windows apropriada para o controle ListView.

Aqui estão os detalhes: http://www.codeproject.com/KB/list/listviewxp.aspx

Em WinForms 2.0 existe uma propriedade protegida chamada DoubleBuffered.

Ao herdar de ListView, então pode-se definir esta propriedade protegida para true. Isso permitirá que o buffer duplo sem a necessidade de chamar SendMessage.

A configuração da propriedade DoubleBuffered é o mesmo que definir o seguinte estilo:

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

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

Eu sei que esta questão é bastante antiga, mas porque este é um dos primeiros resultados de pesquisa no Google eu queria compartilhar a minha correção.

A única maneira que poderia remover cintilação 100% foi a de combinar a resposta de Oliver (classe de extensão com a memória intermédia dupla) e usando os métodos e BeignUpdate() EndUpdate().

Nem daqueles em seu próprio poderia corrigir piscando para mim. Concedido, eu uso uma lista muito complexa, que eu preciso para empurrar para a lista e também precisa atualizá-lo quase todos os seg.

Isso vai ajudar:

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

Uma solução simples é esta:

yourlistview.BeginUpdate ()

// Faça a sua atualização de adicionar e remover item da lista

yourlistview.EndUpdate ()

Se você só quiser atualizar o texto, basta definir o texto de SubItem alterado diretamente, em vez de atualizar todo o ListViewItem (você não disse como você está fazendo suas atualizações).

O override você mostra é equivalente a simplesmente substituindo OnPaintBackground, o que seria uma forma "mais correta" conseguiu fazer essa tarefa, e não vai ajuda para um único item.

Se você ainda tiver problemas, vamos precisar de esclarecimentos sobre o que você realmente tentei.

Este é um tiro no escuro, mas você pode tentar o buffer duplo de controlo.

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

Chame o método BeginUpdate () no ListView antes de definir qualquer um dos itens de exibição de lista e, em seguida, chamada apenas EndUpdate () depois que todos os itens foram adicionados.

Isso vai parar a cintilação.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top