Anzeige eines dialogfeldbasierenden Programms aktualisieren



  • Guten Tag

    Bei einfachen Dienstprogrammen die ich "einfach mal schnell" schreibe um Ordner aufzuräumen, Dateien einzusortieren,.... mache ich mir oft nicht die Mühe um einen Worker-Thread zu erstellen.

    Leider führt das oft dazu, das Anzeigen wie Progress Bar, Textfelder,... nicht aktualisiert werden solange das Programm durchläuft.

    Gibt es eine Möglichkeit die Anzeige aktualisieren zu lassen auch wenn man gerade dabei ist alles in einer Schleife abzuarbeiten?

    Die üblichen Kandidaten wie UpdateData(FALSE); funktionieren da leider nicht.

    Es kann ruhig quick & dirty sein, da diese Programme nie meinen PC verlassen und nur ein paar mal von mir genutzt werden.

    Mein Google-Foo ist leider heute leider nicht stark wessewegen ich die richtigen Suchparameter nicht finde.



  • Du kannst dem Fenster (oder einzelnen Controls) per SendMessage eine WM_SETREDRAW Botschaft schicken. Das sorgt dafür, dass das Fenster/Control sofort neu gezeichnet wird. Bei der MFC gibt es bestimmt eine entsprechende Funktion dafür, wenn du nicht bis auf die WinAPI runter möchtest.
    Das, was Martin sagt...


  • Gesperrt



  • @rbs2 sagte in Anzeige eines dialogfeldbasierenden Programms aktualisieren:

    Probiers mal damit: https://msdn.microsoft.com/en-gb/vstudio/t1tkd768

    Das ist gefährlich. Wenn der Anwender den Dialog schließt während die Schleife läuft passieren schlimme Dinge. Muss man drauf achten.


  • Mod

    Wenn es nur um einen schnellen Update geht, dann genügt CWnd::UpdateWindow(NULL,NULL,RDW_ALLCHILDREN|RDW_UPDATENOW) auf den Dialog auszuführen.

    @RBS2 Die Nutzung von PumpMessage kann ich nicht anraten. Das macht das Programm reentrant. Der Tipp so (alleine und isoliert) ist gefährlich.
    Das kann man machen, wenn man vorher die gesamte UI ansonsten mit EnableWindow(FALSE) blockiert.

    NUR DANN kann man eine eigene weitere Message Pump zulassen. Das bedeutet aber auch das Timer und anderes auch zuschlagen...

    @DocShoe WM_SETREDRAW macht gar nichts von dem was Du schreibst. Bei neuen Controls wird es fast immer ignoriert. Bei anderen Controls setzt oder löscht es nur ein Flag. Gemäß Doku muss man selbst den Redraw / das Update ausführen.



  • Danke für die Ratschläge.

    CWnd::UpdateWindow hat bei mir keine Parameter die ich übergeben könnte.
    Das MSDN kennt bei UpdateWindow auch keine Parameter.

    Und UpdateWindow ohne Parameter funktioniert nicht so richtig.

    Es werden nur manche Anzeigen aktualisiert.
    Einzelne Anzeigeelemente werden leider erst nach Beendigung der Funktion aktualisiert.

    Invalidate vor dem UpdateWindow aufzurufen ist leider auch nicht Zielführend.


  • Mod

    Vertan. Heißt RedrawWindow!



  • Ich habe jetzt RedrawWindow(NULL,NULL,RDW_ALLCHILDREN|RDW_UPDATENOW); ausgetestet.

    Es zeigt ähnliches Verhalten wie UpdateWindow.
    Es werden also ein paar Elemente nicht aktualisiert.
    (EnableWindow Änderungen werden nicht angezeigt und manche Änderungen der Element-Beschriftung.)

    Das sind aber nur ein paar kosmetische Fehlerchen die bei diesen Programmen vollkommen egal sind.

    Danke für eure Hilfe.



  • Alles RedrawWindow oder andere Funktionen werden da kein Erfolg bringen, da Windows ein Nachrichtenbasiertes System ist und die ganzen Aufrufe werden in SendMessages umgewandelt. Also nützt nur eins der MessagePump Rechenleistung zu geben, denn solange du in deiner schleife deinen Kram abarbeitest bekommt die keine Rechenleistung, is ja alles ein Thread. Deswegen kannst du nur in deiner schleife oder was auch immer du machst regelmäßig die Messages mit abarbeiten lassen in dem du folgendes einfügst

     MSG messages;
     while (PeekMessage (&messages, NULL, 0, 0, PM_REMOVE))
        {
        	TranslateMessage (&messages);
        	DispatchMessage (&messages);
        }
    

    Das sollte dann deine nachrichten auch "verarbeiten" lassen und deine Anzeige aktualisieren. Aber ein eigener Thread is halt besser.


  • Gesperrt

    Oder das Programm so umgestalten, dass niemals viel Rechenzeit am Stück verbraten wird, also keine längeren Schleifen oder Blocking-Calls in einem Message-Handler machen.



  • @CTecS Danke für den Tipp
    Damit funktionierts jetzt perfekt.

    Natürlich muss ich da vor dem Abarbeiten alles sperren damit die diversen Schaltflächen nicht mehrfach gedrückt werden können.

    @RBS2
    Es geht darum einfache Q&D Programme dazu zu bringen mir etwas zu zeigen.
    Programme die ich öfter verwende bzw. auch von anderen verwendet werden bekommen natürlich einen Arbeits-Thread.

    Es geht mir hier aber um Programme, die nichteinmal eine ordentliche Eingabeüberprüfung erhalten weil sie nur einmal kurz etwas erledigen sollen und dann nie wieder verwendet werden.
    Da läuft einfach alles einmal durch und fertig.
    Bei diesen Programmen will ich einfach nicht zu viel Aufwand betreiben.


  • Mod

    @ctecs Das stimmt nicht.

    UpdateWindow/RedrawWindow senden direkt die Nachrichten. Dafür ist keine Message Loop nötig... SendMessage wird direkt verwendet.
    Warum auch nicht? WM_PAINT ist auch nur eine synthetisierte Nachricht, die durch PeekMessage erzeugt wird und direkt durch SendMessage ausgeliefert wird. Was anderes macht UpdateWindow auch nicht.
    Schau Dir das im Spy++ an.

    Nur als Info: Man sollte in keinem Fall in einem MFC Programm diese normale Nachrichtenschleife benutzen, die Du zeigst. Auch das wäre fatal, denn dann werden Nachrichten nicht über PreTranslateMessage weitergeleitet und einiges anderes in der MFC geht dann auch nicht.

    Man sollte also immer etwas in dieser Form nutzen:

    while(::PeekMessage(&msg,NULL,NULL,NULL,PM_NOREMOVE))
      AfxPumpMessage();
    

    Das Problem ist eher ein grundsätzliches seit Vista: Wir haben heute noch den DWM (Desktop Window Manager), der einige Aktualisierungen nicht durchlässt (oder anzeigt).
    Klassisch ohne DWM geht das.
    Zitat:

    When desktop composition is enabled, individual windows no longer draw directly to the screen or primary display device as they did in previous versions of Windows.

    Die Grundaussage stimmt: Mit Sicherheit bekommt man Updates nur über eine Nachrichtenschleife hin. Aber nicht mit der Begründung, die Du gegeben hast, sondern weil der DWM dann auch zum Zuge kommt.
    In jedem System ohne DWM langt ein UpdateWindow/RedrawWindow.

    Vielleicht hilft ein zusätzlicher DWMFlush... habe es noch nie ausprobiert... Weiterhin es es auch so, dass mit Common Control 6 alle Controls auch mit der Existenz des DWMs rechnen... und das führt eben zum cachen des DCs und vielen anderen Effekten...

    Zu DWM siehe auch hier



  • @Martin-Richter
    Danke für den Hinweis.
    Ich hatte durch den Hinweis auf PeekMessage von @CTecS schon Beispiele gefunden die PumpMessage verwendet haben.

    Ich verwende eine "Basis-Dialogklasse" von der ich alle meine Q&D Projekte ableite.

    Und dort habe ich folgene Funktionen hinzugefügt:

    // Nachrichten abarbeiten
    void CDialogBasis::ProcessMessages()
    {
        ASSERT(m_bPumpMode);
    
        MSG msg;
        CWinApp* pApp = AfxGetApp();
        while (PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE))
        {
            pApp->PumpMessage();
        }
    }
    
    // Alle Schaltflächen (CButton & davon abgereitet) deaktivieren
    // und OnOK / OnCancel mittels m_bPumpMode abschalten
    void CDialogBasis::PumpModeOn()
    {
        m_bPumpMode = TRUE;
        CMyGuiClass::AllButtonDisable(this);
    }
    
    // Alle Schaltflächen (CButton & davon abgereitet) aktivieren
    // und OnOK / OnCancel mittels m_bPumpMode einschalten
    void CDialogBasis::PumpModeOff()
    {
        m_bPumpMode = FALSE;
        CMyGuiClass::AllButtonEnable(this);
    }
    

    AllButtonEnable(this); und AllButtonDisable(this); sind statische Funktionen, in der mittels GetDlgItem alle Dialogelemente abgearbeitet werden.
    Wenn der Klassenname eines Elements BUTTON enthält, wird dieses Element deaktiviert bzw. aktiviert.


Anmelden zum Antworten