Laden...

zu schnelles/häufiges Aktualisieren der Oberfläche verhindern

Erstellt von Froggie vor 10 Jahren Letzter Beitrag vor 10 Jahren 2.708 Views
F
Froggie Themenstarter:in
323 Beiträge seit 2007
vor 10 Jahren
zu schnelles/häufiges Aktualisieren der Oberfläche verhindern

Hallo!
Ich habe ein Fenster welches mir den Fortschritt von bestimmten Operationen anzeigen kann. Dazu bekommt es einfach die anzuzeigenden Daten von den jeweiligen Controllern für diese Operation überliefert.
Nun können die Controller aber "sehr geschwätzig" sein und mehrmals pro Sekunde neue Daten senden (per Event). Dann kommt die Oberfläche nicht mehr mit bzw. flackert sehr oft (Labels werden aktualisiert).
Da es sich ja um ein Problem der Oberfläche und nicht der Controller handelt, würde ich das Problem ganz gerne an der Oberfläche (bzw. in dem Controller der Oberfläche) beheben.

Wie kann ich es verhindern das es mehrmals pro Sekunde neue Daten gibt?
Mein erster Gedanke war ein Timer, aber das kommt mir zu kompliziert vor. Gibt es da irgendwelche Musterlösungen oder Best Practices?

Vielen Dank im Voraus!

106 Beiträge seit 2011
vor 10 Jahren

Hallo Froggie,

Ein Timer oder Backgroundworker wäre schon der richtige Ansatz, übertrage deine Infos einfach wie gewohnt mehrmals in der Sekunde über die Events aber aktualisier die Controls in deiner Form nur 1 mal in der Sek.

MfG
Rabban

C
258 Beiträge seit 2011
vor 10 Jahren

Du kannst wenn du das ganze im Controller des Forms machen willst einfach die werte der Events auf Instanzvariablen und nicht direkt auf Controls legen und per Timer dann "weitergeben"
EDIT: Mir ist gerade aufgefallen das mein Timer vorschlag genau das gleich ist wie Rabban schon geschrieben hat..

Jedoch kann ich mir nicht vorstellen das es ein Problem sein soll "mehrmals die sekunde" einige Controls zu aktualisieren. Wie oft ist bei dir mehrmals?

Bei mir funktionieren bis zu 30 Updates die Sekunde bei ~ 15 Controls die eine Änderung zeigen ohne Probleme / flackern (auch bei einem SingleCore Prozessor mit unter 1Ghz/Energiesparmodus)

D
500 Beiträge seit 2007
vor 10 Jahren

Hallo!

Ich wuerde jetzt erst einmal fragen, was sind das Events und warum kommen diese so haeufig? Ist es wirklich notwendig, dass diese so haeufig hochkommen?

Dann faellt mir natuerlich noch das Stichwort DoubleBuffering ein:
DoubleBuffering

Gruss,
Moe

C
2.121 Beiträge seit 2010
vor 10 Jahren

Ich könnt mir vorstellen, das Problem ist nicht unbedingt die grafische Darstellung an sich, sondern man kann mit einer Anzeige einfach nichts anfangen wenn sie sich zu schnell ändert.

Was ist an einem Timer so kompliziert? Rabbans Vorschlag würde ich auch als die Lösung sehen.
Merk dir die Labeltexte separat wenn Daten eingehen. In einem Timer Event schreibst du die dann in die Labels. Die Geschwindigkeit der Aktualisierung kannst du dann sogar im Formular einstellbar machen, so dass man sie jederzeit nach Wunsch ändern kann.

C
168 Beiträge seit 2010
vor 10 Jahren

Dein Label flackert weil der Inhalt sich häufig ändert diesen manuell zu verlangsamen ist nicht sehr sinnvoll, da du den Fortschritt noch mehrere Sekunden vielleicht sogar Minuten anzeigst obwohl der Prozess/Ablauf schon längst fertig ist. Du würdest damit sogesehen die Ladezeit künstlich erhöhen ... und über lange Ladezeit freut sich keiner 😃.

Aber es gibt eine meiner Meinung nach ziemlich gute Lösung nämlich indem du Prozessteile zusammenfasst und diesen zusammen gefassten Prozessteil gibst du einen Namen bzw. Text den du dafür einzeigen möchtest.

Beispiel:



int i = 1;
string Tag = "lese Dateien";
While(i < 100)
{
     //statt jede einzelne Zeile als Fortschritt auszugeben oder jeden einzelne Datei setzt du einfach vor der While schleife das Tag "lese Dateien"
     
     //das Tag welches den Fortschritt ausgibt nicht in der While schleife oder in Foreach verändert, durch diese wird es zuhäufig aktualisiert.
     string[] lines = File.ReadAllLines("C:/temp" + i.ToString() + ".txt");
     foreach(string line in lines)
     {
          //DoSomething
     }
}

//Hier wird ein neues Tag gesetzt für den nächsten zusammengefasste Prozess
Tag = "Berechne";
...

So mach ich es zu mindestens.

Mfg Crone

Real programmers don't comment their code - it was hard to write, it should be hard to understand.

106 Beiträge seit 2011
vor 10 Jahren

Du würdest damit sogesehen die Ladezeit künstlich erhöhen

Das sehe ich nicht so. Seine Aufgabe kann der GUI einfach mitteilen wenn die Aktion abgeschlossen ist, die löst eine letzte Aktualisierung aus und schon ist das GUI wieder synchron.

Der Weg den Crone aufgezeigt hat, geht natürlich auch, allerdings stellt er viel weniger Informationen dar, es hängt also ab ob man so viel benötigt oder nicht.

MfG
Rabban

463 Beiträge seit 2009
vor 10 Jahren

Hmm, und wo ist jetzt deiner Meinung nach der Unterschied zwischen zuwenig Daten und abgeschnitte Daten am Schluss? Ich sehe hier die Zusammenfassung der Daten eher als plausibel an, als einfach am Schluss den Rest zu unterschlagen....

106 Beiträge seit 2011
vor 10 Jahren

Wieso sollten die Daten zum schluss abgeschnitten sein?
So wie ich es skizziert habe, sind alle Daten zum Schluss vorhanden und auch in der GUI.

MfG
Rabban

F
Froggie Themenstarter:in
323 Beiträge seit 2007
vor 10 Jahren

Hallo!
Vielen Dank für die Vorschläge!
Ich habe mich jetzt für eine Lösung entschieden und die funktioniert auch ausreichend für meinen Fall. Geht vielleicht eleganter, aber "It just works." war erst mal wichtiger.

Bevor ich die Daten an die View weitergebe, prüfe ich wie lange die letzte Weitergabe her ist.


private const int UPDATE_INTERVAL = 150;
private DateTime lastUpdateCurrentStatus = DateTime.MinValue;

//Das Event das aufgerufen wird
public void SetProgressCurrentStatus(ProgressStatusEventArgs args)
        {
            if (canUpdateCurrentStatus())
            {
                view.SetProgressCurrentStatus(args);
            }
        }

//Aktualisierung nur alle 150ms zulassen
private bool canUpdateCurrentStatus()
        {
            var result = false;

            var now = DateTime.UtcNow;
            var diff = now - lastUpdateCurrentStatus;
            if (diff.Milliseconds > UPDATE_INTERVAL)
            {
                lastUpdateCurrentStatus = now;
                result = true;
            }

            return result;
        }