2009-01-14 7 views
44

Ich habe eine Listenansicht, die regelmäßig aktualisiert wird (alle 60 Sekunden). Es war mir unangenehm, dass ich jedes Mal ein Flimmern bekommen würde, wenn es veraltet war. Die verwendete Methode war, alle Elemente zu löschen und sie dann neu zu erstellen. Ich entschied mich dafür, die Sachen nicht zu löschen, sondern direkt in die Zelle mit dem neuen Text zu schreiben. Ist das ein besserer Ansatz oder hat jemand eine bessere Lösung?C# flackern Listview auf Update

Antwort

77

Das ListView-Steuerelement hat ein Flicker-Problem. Das Problem scheint zu sein, dass die Überladungsüberladung des Steuerelements nicht ordnungsgemäß implementiert ist, so dass es sich wie ein Aktualisieren verhält. Ein Update sollte dazu führen, dass das Steuerelement nur seine ungültigen Regionen neu zeichnet, während ein Refresh den gesamten Clientbereich des Steuerelements neu zeichnet. Wenn Sie beispielsweise die Hintergrundfarbe eines Elements in der Liste ändern, muss nur dieser bestimmte Artikel neu gezeichnet werden. Leider scheint das ListView-Steuerelement eine andere Meinung zu haben und möchte seine gesamte Oberfläche neu streichen, wenn Sie sich mit einem einzelnen Objekt anlegen ... selbst wenn das Objekt gerade nicht angezeigt wird. Also, sowieso, können Sie leicht das Flimmern unterdrücken, indem Sie Ihre eigenen rollen wie folgt:

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

Von: Geekswithblogs.net

+1

Sie müssen wissen, dass der Nachteil dieser Lösung (aufgrund der doppelten Pufferung) ist, dass die Aktualisierung der Kontrolle wesentlich langsamer ist (versuchen Sie 1000 Elemente hinzuzufügen). –

+1

Um ehrlich zu sein, verwende ich nie die Winforms Listview. Aber ist dieser Leistungsverlust nicht weg, wenn Sie ** SuspendLayout ** und ** ResumeLayout ** anrufen? – Stormenet

+0

Danke Stormenent. Ich habe eine Weile nach einer Lösung für dieses Problem gesucht und das funktioniert gut für mich. –

4

Ja, machen es doppelt gepuffert. Es wird das Flimmern reduzieren;) http://msdn.microsoft.com/en-us/library/system.windows.forms.listview.doublebuffered.aspx

+1

-1: Sie können keine DoubleView-Eigenschaft von DoubleView (es ist geschützt) festlegen. –

+1

-1 zu Ihrem Kommentar, weil Sie es einrichten können, wenn Sie eine neue Klasse daraus ableiten, gibt es auch die "SetStyle" -Methode. Aber deine Art zu recherchieren, bevor du jemanden abstimmst, selbst wenn du mit dem genauen Grund geantwortet hast, warum dein Kommentar keinen Sinn macht * Daumen hoch * – SilverX

+0

erinnere dich einfach, SetStyle ist auch geschützt ... wie hast du es geschafft, das zu nennen? – SilverX

0

Versuchen Sie, die doppelt gepufferte Eigenschaft in true zu setzen.

Auch könnten Sie verwenden:

this.SuspendLayout(); 

//update control 

this.ResumeLayout(False); 

this.PerformLayout(); 
+0

Layout gilt hauptsächlich beim Verschieben/Ändern der Größe ... Ich vermute, Sie meinen [Begin | End] Update, wie in meiner Antwort. –

21

Zusätzlich zu den anderen Antworten, haben viele Kontrollen eine [Begin|End]Update() Methode, die Sie verwenden können, Flimmern zu reduzieren, wenn die Inhalte der Bearbeitung - zum Beispiel:

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

+1: Danke; Das machte einen deutlichen Unterschied :-) –

+0

Vielen Dank! Ich habe das Internet nach oben und unten durchsucht, und keines der Suchergebnisse ist anwendbar: D – pepoluan

+1

Ich habe festgestellt, dass dies hilft, aber das Flimmern nicht vollständig beseitigt. – usr

4

Ausgezeichnete Frage und Stormenents Antwort war genau richtig. Hier ist ein C++ - Port seines Codes für alle anderen, die sich mit C++/CLI-Implementierungen beschäftigen.

#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

Wenn dies kann helfen, gelöst die folgende Komponente meine Listview-Flimmern Probleme mit .NET 3,5

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

Ich benutze es in conjonction mit .BeginUpdate() und .EndUpdate() -Methoden, wo ich tun ListView.Items-Manipulation.

Ich verstehe nicht, warum diese Eigenschaft eine eine geschützte ist ... auch in .NET 4.5 (möglicherweise ein Sicherheitsproblem)

1

Einfache Lösung

yourlistview.BeginUpdate() 

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

yourlistview.EndUpdate() 
+0

Dies funktionierte für mich, als ich eine Listenansicht hatte, die ich auf 100 Elemente beschränkt hatte, und ich löschte die erste Zeile beim Hinzufügen einer neuen Zeile, wenn dieser Schwellenwert erfüllt war. –

3

Die einfachste Lösung wäre sein wahrscheinlich

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

statt

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

Das funktioniert viel besser.

+0

Vielen Dank hatte wirklich Probleme mit dem Blockieren meiner kompletten GUI mit nur das Hinzufügen von Elementen, die es löste. –

0

Ich weiß, das ist ein extrem alte Frage und Antwort. Dies ist jedoch das beste Ergebnis bei der Suche nach "C++/cli Listview Flicker" - trotz der Tatsache, dass hier nicht einmal von C++ gesprochen wird.Also hier ist die C++ Version davon:

Ich habe dies in der Header-Datei für mein Hauptformular, können Sie es an anderer Stelle setzen ...

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

Wenn Sie geschehen, hier zu landen für eine Suche ähnliche Antwort für verwaltetes C++, das funktioniert für mich. :)

0

In Winrt Windows Phone 8.1 können Sie den folgenden Code festlegen, um dieses Problem zu beheben.

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

Hier ist meine schnelle Lösung für eine C# -Implementierung, die erfordert nicht die Listenansichten Subklassen usw.

Reflexion Verwendet die DoubleBuffered Eigenschaft setzen in den Formen Konstruktor zu versuchen.

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