Painting only when your window is visible on the screen


Es kommt häufiger mal vor, dass man dem Benutzer ein visuelles Feedback geben will, wie weit eine Aufgabe schon fortgeschritten ist, entweder beim Kopieren einer Datei an Hand eines Fortschrittbalkens oder ähnliches. Jetzt ist es natürlich blöd, wenn man dies auch tut, wenn das Fenster gar nicht sichtbar ist, da man unnötig Performance verschwendet, obwohl der Benutzer es gar nicht sehen kann. Ähnlich funktioniert auch die Uhr in der TNA. Die Uhr in der TNA aktualisiert sich nur, wenn sie auch sichtbar ist. Ist sie nicht sichtbar, weil die Taskbar automatisch verschwindet oder nicht immer alwaysOnTop ist oder überdeckt wird, dann aktualidiert sie sich nicht und ist idle. Sioe macht dies nach folgendem Prinzip:

  1. Sie berechnet die Zeit bis zur nächsten Sekunden
  2. Es wird ein Timer erzeugt mit der Zeit, wie lange gewartet werden muss
  3. Wenn der Timer ausgelöst wird, wird das Fenster für ungültig erklärt (eine WM_PAINT Nachricht wird an das Fenster geschickt) und der Timer selber wird zerstört.
  4. Im WM_PAINT Zweig der Fensterprozedur wird das Fenster akualisiert und mit Schritt 1 weitergemacht.

Dieses Vorgehen hat zur Folge, dass wenn das Fenster nicht sichtbar ist, es auch nichts macht.

Ich will dies an einem Beispiel in Delphi deutlich machen. Da wir eigentlich nur ein Fenster und einen Timer brauchen, verwende ich der Übersichtlichkeit wegen ein einfaches nonVCL Programm, verzichte also auf die VCL und benutze nur Windows API Funktionen. Für die jenigen, die bisher mit der Win API Programmierung unter Deelphi noch nicht allzuviel zu tun hatten, seien an dieser Stelle meine Win32 API Tutorials für Delphi ans Herz gelegt, insbesondere Kapitel eins ist hier von Interesse bzw. mein Artikel Wie Windows Funktioniert Hier aber noch mal kurz zusammengefasst, damit man das Demo Programm versteht:

Das Hauptprogramm besteht aus einer Endlosschleife, die einfach nur Nachrichten aus der Nachrichtenschleife abholt. Diese leitet sie dann weiter an die Fensterprozedur, wo auf die verschiedenen Nachrichten reagiert wird oder auch nicht. Nehmen wir als Beispiel die Nachricht WM_PAINT. Diese wird von Windows an unser Fenster geschickt, wenn es sich neuzeichnen muss, weil ein Bereich oder das ganze Fenster ungültig geworden ist. Die Nachrichtenschleife gibt die Nachricht an unsere Fensterprozedur weiter, welche im Case-Zweig mit einem Neuzeichnen des Fenster reagiert.

Unser Beispiel Programm gibt zur Demonstration im Client Bereich des Fensters nur einfach die aktuelle Zeit aus.

Um das Neuzeichnen etwas effizienter zu gestallten, reagiere wir im WM_PAINT mit einem Neuzeichen nur, wenn der Bereich, in dem die Zeit ausgeben wird, neugezeichnet werden muss:

    WM_PAINT:
      begin
        BeginPaint(hWnd, ps);
        if RectVisible(ps.hdc, TimeRect) then
        begin
          GetSystemTime(st);
          dwTimeToNextTick := 1000 - st.wMilliseconds;
          SetTimer(hWnd, 1, dwTimeToNextTick, @TimerCallback);
        end;

        PaintContent(hWnd, ps.hdc);

        EndPaint(hWnd, ps);
      end;

Ob unser Bereich betroffen ist oder nicht ermitteln wir mit der API Funktion RectVisible. Muss es neugezeichnet werden, ermitteln wir die aktuelle Systemzeit, berechnen die Dauer bis zur nächsten Sekunde und erzeugen den Timer. In der Timer Callback Funktion wird der Timer einfach wieder zerstört und InvalidateRect aufgerufen, um ein Neuzeichnen des Fensters zu veranlassen:

procedure TimerCallback(hWnd: THandle; uMsg: UINT; IDTimer: UINT; dwTime: DWORD); stdcall;
begin
  KillTimer(hWnd, IDTimer);
  InvalidateRect(hWnd, @TimeRect, False);
end;

Das Ausgeben der aktuellen Zeit geschiet dann mit der Prozedur PaintContent. Zur Kontrolle wird die aktuelle Zeit auch noch mal in der Titelleiste des Fensters angezeigt. Wie man beobachten kann bleibt sie stehen, so bald der Bereich, in der die Zeit ausgegeben wird, nicht mehr sichtbar ist.

Das war eigentlich schon alles. Hier kann man sich noch mal das vollständige Demoprogramm runterladen. Wie man sieht, sind es häufig die Details, die aus einer Anwendung, eine Anwendung mit dem gewissen Etwas machen.

Dieser Artikel beruht auf einem Blog-Eintrag von Raymond Chen: Painting only when your window is visible on the screen.

2010-12-29T23:44:52 +0100, mail+homepage[at]michael-puff.de