myCSharp.de - DIE C# und .NET Community
Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 
 | Suche | FAQ

» Hauptmenü
myCSharp.de
» Startseite
» Forum
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Suche
» Regeln
» Wie poste ich richtig?
» Forum-FAQ

Mitglieder
» Liste / Suche
» Wer ist wo online?

Ressourcen
» openbook: Visual C#
» openbook: OO
» Microsoft Docs

Team
» Kontakt
» Übersicht
» Wir über uns
» Datenschutzerklärung
» Impressum

» myCSharp.de Diskussionsforum
Du befindest Dich hier: Community-Index » Diskussionsforum » Knowledge Base » Artikel » [Artikel] Flackernde Controls und flackerndes Zeichnen vermeiden
Letzter Beitrag | Erster ungelesener Beitrag Druckvorschau | Thema zu Favoriten hinzufügen

Antwort erstellen
Zum Ende der Seite springen  

[Artikel] Flackernde Controls und flackerndes Zeichnen vermeiden

 
Autor
Beitrag « Vorheriges Thema | Nächstes Thema »
JAck30lena JAck30lena ist männlich
myCSharp.de-Team

avatar-2653.jpg


Dabei seit: 01.10.2006
Beiträge: 11.397
Entwicklungsumgebung: Visual Studio 05/08/10 Prof.


JAck30lena ist offline

[Artikel] Flackernde Controls und flackerndes Zeichnen vermeiden

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Flackern eines Controls kann im Wesentlichen in 2 voneinander unabhängigen Fällen auftreten.

Fall 1:

Wenn man OnPaint(...) überschreibt, viel selbst zeichnet und auch noch im z.B. MouseMove-Ereignis eine komplette Neuzeichnung (.Invalidate()) anstößt, kann es vorkommen, dass sich das Steuerelement in hässliches Flackern hüllt.

Fall 2:

Ebenso kann es beim Befüllen eines DataGridViews, ListView, ListBox, beim Hinzufügen von vielen Controls zu einer Form oder einem anderen Control, dazu kommen, dass es hässlich flackert, bis das Befüllen abgeschlossen ist.


Zum Fall 1:

1. Nie in einem MouseMove-Ereignis ein komplettes .Invalidate() ohne Angabe einer eingegrenzten Region aufrufen.

Wenn man z.B. ein Gummiband (Rubberband) zeichnen möchte, damit dem User visuell angezeigt wird, dass er gerade einen Rahmen um etwas zieht, dann ist es besser ein Invalidate(...) auf die Größe des Rahmens zu beschränken. Natürlich würde bei Selektion in Größe des Controls ein komplettes Neuzeichnen verlangen, aber in den meisten Fällen selektiert der User einen kleineren Bereich. (Alternativ kann man für jede Seite des alten und des neuen Rechtecks nur den Bereich .Invalidate(...) machen. Also achtmal insgesamt.)

2. Setzen der richtigen  style-flags mit der  SetStyle(...) Methode für das Form und für alle flackernden Controls.
  1. die .DoubleBuffered Eigenschaft des Controls

    Zitat von MSDN:
    Das bevorzugte Verfahren zum Aktivieren der Doppelpufferung mit gleichem Ergebnis besteht jedoch darin, die DoubleBuffered-Eigenschaft für das Steuerelement auf true festzulegen.

    Soll heißen: bevor man mit SetStyle arbeitet um Flimmern zu vermeiden, soll man die DoubleBuffered-Eigenschaft auf true setzen und schauen, ob das Problem immer noch auftritt.

    DoubleBuffered-Eigenschaft ist "protected" und kann daher nur bei eigenen oder abgeleiteten Controls auf "true" gesetzt werden außer bei einer "Form" und da gilt: wenn die "Form" DoubleBuffered ist, Flackern die Controls auch weiterhin, da ausschließlich die Form buffert, nicht aber die Controls. DoubleBuffered muss also auch für alle flackernden Controls gesetzt werden.

  2. SetStyle(ControlStyles.DoubleBuffer,true)

    Zitat von MSDN:
    Wenn true, wird der Zeichenvorgang in einem Puffer ausgeführt und das Ergebnis nach Beendigung auf dem Bildschirm ausgegeben. Durch Doppelpufferung wird Flimmern durch das Neuzeichnen des Steuerelements verhindert.

    Besser gesagt... es verringert das Flimmern. Manchmal reicht es nicht aus.

    Hinweis: Dieses Flag ist für die Intellisence ausgeblendet, da es nur noch aus Kompatibilitätsgründen zu .net 1.x enthalten sein muss. Statt diesem Flag sollte man lieber "ControlStyles.OptimizedDoubleBuffer" setzen.

  3. SetStyle(ControlStyles.OptimizedDoubleBuffer,true)

    Siehe Punkt a.

  4. SetStyle(ControlStyles.CacheText,true)

    Zitat von MSDN:
    Wenn true, bewahrt das Steuerelement eine Kopie des Textes auf, sodass dieser nicht jedes Mal, wenn er benötigt wird, aus Handle abgerufen werden muss. Dieser Stil hat den Standardwert false. Dieses Verhalten verbessert die Leistung, erschwert jedoch die Textsynchronisierung.

  5. SetStyle(ControlStyles.AllPaintingInWmPaint,true)

    Zitat von MSDN:
    Wenn true, ignoriert das Steuerelement die WM_ERASEBKGND-Fenstermeldung, um das Flimmern zu verringern. Dieses Format sollte nur angewendet werden, wenn das UserPaint-Bit auf true festgelegt wurde.

  6. SetStyle(ControlStyles.UserPaint,true)

    Zitat von MSDN:
    Wenn true, zeichnet sich das Steuerelement selbst, sodass es nicht vom Betriebssystem gezeichnet werden muss. Wenn false, wird das Paint-Ereignis nicht ausgelöst. Dieser Stil wird nur auf von Control abgeleitete Klassen angewendet.

    Zitat von MSDN:
    Wenn das AllPaintingInWmPaint-Bit auf true festgelegt ist, wird die WM_ERASEBKGND-Fenstermeldung ignoriert, und sowohl die OnPaintBackground-Methode als auch die OnPaint-Methode werden direkt aus der WM_PAINT-Fenstermeldung aufgerufen. Dadurch wird das Flimmern i. d. R. verringert, sofern keine anderen Steuerelemente die WM_ERASEBKGND-Fenstermeldung an das Steuerelement senden. Sie können die WM_ERASEBKGRND-Fenstermeldung senden, um einen pseudotransparenten Effekt, ähnlich wie SupportsTransparentBackColor, zu erzeugen. Dies wird z. B. bei ToolBar in flacher Darstellung verwendet.

    Damit die Doppelpufferung vollständig aktiviert wird, können Sie auch die Bits OptimizedDoubleBuffer und AllPaintingInWmPaint auf true festlegen. Das bevorzugte Verfahren zum Aktivieren der Doppelpufferung mit gleichem Ergebnis besteht jedoch darin, die DoubleBuffered-Eigenschaft für das Steuerelement auf true festzulegen.

Zum Fall 2:

Gerade unter Windows Forms ist die Anzahl von Controls, die in einem Form / einer Anwendung ohne Performance-Einbußen und ohne Flackern dargestellt werden kann, stark begrenzt. Unter Umständen sind schon 100 Controls zu viel. Wenn das der Grund für das Flackern ist, hilft es nur, die Anzahl der Controls zu reduzieren. Dazu gibt es viele Möglichkeiten, vom simplen Entschlacken der Form, über das Verwenden von Listen-Controls statt von eigenen Controls für die einzelnen Listeneinträge bis zum Selberzeichnen von Controls und Inhalten (siehe  [Tutorial] Zeichnen in Windows-Forms-Programmen (Paint/OnPaint, PictureBox)).

Wenn es nur beim Hinzufügen/Umordnen der Controls flackert: Jedes dieser Steuerelemente hat eine Methode  SuspendLayout() und eine  ResumeLayout().

Zitat von MSDN:
Die Layoutlogik des Steuerelement wird unterbrochen, bis die ResumeLayout-Methode aufgerufen wird.

Die SuspendLayout-Methode und die ResumeLayout-Methode werden zusammen verwendet, um mehrere Layout-Ereignisse zu unterdrücken, während mehrere Attribute des Steuerelements angepasst werden. Beispiel: die SuspendLayout-Methode wird aufgerufen, dann werden die Eigenschaften Size, Location, Anchor und/oder Dock des Steuerelements festgelegt und schließlich wird die ResumeLayout-Methode aufgerufen, damit die Änderungen wirksam werden.

Das bedeutet, das man vor dem Befüllen einfach ein SuspendLayout(...) macht und nach dem Befüllen ein ResumeLayout(...). Schon wird das Flackern vermieden.

Selbiges gilt beim Hinzufügen mehrerer Controls zu einem Control:

Zitat von msdn:
Wenn Sie mehrere Steuerelemente einem übergeordneten Steuerelement hinzufügen, empfiehlt es sich, vor dem Initialisieren der hinzuzufügenden Steuerelemente die SuspendLayout-Methode aufzurufen. Rufen Sie die ResumeLayout-Methode auf, nachdem die Steuerelemente dem übergeordneten Steuerelement hinzugefügt wurden. Dies erhöht bei vielen Steuerelementen die Leistung der Anwendung.

Manche Controls haben dennoch Spezialitäten:
(dieser teil ist von winSharp93)
  1.  (Checked-)Listbox und  Listview

    Oft findet man Code, der folgendem ähnlich sieht:

    C#-Code:
    for (int i = 0; i < 10000; i++)
        this.listBox1.Items.Add("Item " + i);

    Während der Ausführung (die quälend langsam verläuft) kann man auch hier oft ein unangenehmes Flackern wahrnehmen. Abhilfe schaffen die zusätzlichen Methoden  Begin- und  EndUpdate von CheckedListBox, ListBox sowie ListView. Folgender Code wird sogar um Faktor sechs (!) schneller ausgeführt - das Flackern ist weg.

    C#-Code:
    this.listBox1.BeginUpdate();
    for (int i = 0; i < 10000; i++)
       this.listBox1.Items.Add("Item " + i);
    this.listBox1.EndUpdate();

    Noch schneller geht es übrigens, wenn man die Items.AddRange Methode verwendet.
    Auch hier ein kurzes Beispiel:

    C#-Code:
    List<string> lst = new List<string>();
    for (int i = 0; i < 10000; i++)
       lst.Add(Item " + i);
    this.listBox1.Items.AddRange(lst);

    An dieser Stelle vielen Dank an Maddy für den Hinweis mit AddRange.

  2.  Textbox

    Beginnen wir auch hier mit einem Codebeispiel:

    C#-Code:
    for (int i = 0; i < 500; i++)
       this.textBox1.Text += "Item " + i + Environment.NewLine;

    Auf den ersten Blick scheint der Code keine großen Optimierungsmöglichkeiten zu bieten. Stöbert man etwas in der Doku zur Textbox, stößt man schnell auf die  AppendText Methode:

    C#-Code:
    for (int i = 0; i < 500; i++)
       this.textBox1.AppendText("Item " + i + Environment.NewLine);

    Das Ergebnis kann sich schon jetzt sehen lassen - etwa zwei Drittel der ursprünglichen Dauer lassen sich einsparen.
    Doch das Optimum ist noch lange nicht erreicht. Die Verwendung eines  StringBuilders beschleunigt den Code noch einmal um Faktor 40 (!!!):

    C#-Code:
    StringBuilder sb = new StringBuilder();
    for (int i = 0; i < 500; i++)
       sb.Append("Item " + i + Environment.NewLine);
    this.textBox1.Text = sb.ToString();

    Vergleicht man nun den ursprünglichen mit dem letzten Code, stellt man fest, dass der letzte Code beinahe 200 mal schneller läuft. Außerdem flackert nichts mehr.
Zu guter letzt noch ein paar Links zum Thema:

 [Einführung] Zeichnen Optimieren / Schnelles zeichnen
 GetPixel und SetPixel um Längen geschlagen. 800 mal schneller
 Bitmap-Manipulation (MemBitmap)
 [FAQ] Flackernde Controls vermeiden / Schnelles, flackerfreies Zeichnen
 [Tutorial] Zeichnen in Windows-Programmen (Paint/OnPaint, PictureBox)
 [Tutorial] Gezeichnete Objekte mit der Maus verschieben
 GDI+ & OutOfMemoryException
 [Artikel] Performant Strings verketten

Ein gutes Tutorial zum Einstieg in das Zeichnen mit GDI+ und C#:
 Professional C# - Graphics with GDI+

Und die GDI+ Bibel:
 GDI+ FAQ


Vielen Dank an herbivore und an winSharp93 für die engagierte Unterstützung.

Dieser Beitrag wurde 4 mal editiert, zum letzten Mal von JAck30lena am 18.03.2009 12:40.

25.08.2008 09:10 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Zwischen diesen beiden Beiträgen liegt mehr als ein Monat.
PhilHol
myCSharp.de-Mitglied

Dabei seit: 07.08.2007
Beiträge: 82
Herkunft: Wien, Österreich


PhilHol ist offline Füge PhilHol Deiner Kontaktliste hinzu MSN-Passport-Profil von PhilHol anzeigen

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo,

hab mich gerade bisschen mit Optimierung beschäftigt und, glaube, bin auf einem Fehler bei dir draufgekommen:

Zitat:
Noch schneller geht es übrigens, wenn man die Items.AddRange Methode verwendet.
Auch hier ein kurzes Beispiel:

C#-Code:
List<string> lst = new List<string>();
for (int i = 0; i < 10000; i++)
   lst.Items.Add(Item " + i);
this.listBox1.Items.AddRange(lst);

Sollte das net so sein

C#-Code:
List<string> lst = new List<string>();
for (int i = 0; i < 10000; i++)
   lst.Add(Item " + i);
this.listBox1.Items.AddRange(lst.ToArray());

? Weil dein Beispiel geht so nicht ^^ (finde kein Item Propertie in einer List und keine möglichkeit AddRange mit einer List standardmäßig zu befüllen)

Lg
12.10.2008 22:19 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
JAck30lena JAck30lena ist männlich
myCSharp.de-Team

avatar-2653.jpg


Dabei seit: 01.10.2006
Beiträge: 11.397
Entwicklungsumgebung: Visual Studio 05/08/10 Prof.

Themenstarter Thema begonnen von JAck30lena

JAck30lena ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Zitat:
finde kein Item Propertie in einer List

du hast recht, list hat kein "Item" property aber in diesem beispiel wir auch eine "ListBox" verwendet und die hat eine "Item" property.

Zitat:
keine möglichkeit AddRange mit einer List standardmäßig zu befüllen

alle "AddRange" methoden haben eine überladung, die ein IEnumerable<T> als parameter annehmen. dieses interface ist in allen generischen collections (also auch in List<T> implementiert. Daher kann man an die addrange methode ohne weiteres jede iterierbare generische collection übergeben.
13.10.2008 10:45 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
PhilHol
myCSharp.de-Mitglied

Dabei seit: 07.08.2007
Beiträge: 82
Herkunft: Wien, Österreich


PhilHol ist offline Füge PhilHol Deiner Kontaktliste hinzu MSN-Passport-Profil von PhilHol anzeigen

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Hallo,

will nicht klguscheissen, aber du verwendest in dem von mir zitierten Beispiel die List<string> lst in der for schleife .. und die hat kein Item prop ..

und bei AddRange(lst) kommt immer:

Argument '1': cannot convert from 'System.Collections.Generic.List<string>' to 'object[]'

AddRange() hat 2 Überladungen: object[] und ListBox.ObjectCollection

lg
15.10.2008 11:23 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
JAck30lena JAck30lena ist männlich
myCSharp.de-Team

avatar-2653.jpg


Dabei seit: 01.10.2006
Beiträge: 11.397
Entwicklungsumgebung: Visual Studio 05/08/10 Prof.

Themenstarter Thema begonnen von JAck30lena

JAck30lena ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

ah ja hab das überlesen. du hast recht, sry. -> korrigiert
15.10.2008 18:29 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
winSharp93 winSharp93 ist männlich
myCSharp.de-Poweruser/ Experte

avatar-2918.png


Dabei seit: 19.01.2007
Beiträge: 5.742
Herkunft: Stuttgart


winSharp93 ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Oha - mein Fehler...
Ich fordere eine Zwischenablage mit C# Syntaxprüfungen großes Grinsen
15.10.2008 19:53 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Baumstruktur | Brettstruktur       | Top 
myCSharp.de | Forum Der Startbeitrag ist älter als 10 Jahre.
Der letzte Beitrag ist älter als 10 Jahre.
Antwort erstellen


© Copyright 2003-2019 myCSharp.de-Team | Alle Rechte vorbehalten. | Dieses Portal verwendet zum korrekten Betrieb Cookies. 21.05.2019 08:56