Flackern der Controls bei resize



  • Ich hab einen MFC Dialog mit:
    - einem HTML control auf der linken Seite
    - ein paar Buttons in einer Groupbox unten
    - und einem Zeichenbereich rechts

    Ich will das flackern minimieren. Um das flackern im Zeichenbereich zu minimieren habe ich diese Methode verwendet und bin sehr zufrieden. Hauptproblem sind die Buttons unten, die flackern wie verrückt wenn ich das Fenster in der Größe verändere. (Edit: Das HTML-Control flackert auch aber nicht so stark)

    Vereinfachter Code:

    void CMyDialog::OnSize(UINT nType,int cx,int cy)
    {
     CDialog::OnSize(nType,cx,cy);
     if(!ButtonControl1.GetSafeHwnd()) return;
     if(nType==SIZE_MAXIMIZED || nType==SIZE_RESTORED){
      c_HTML.SetWindowPos(0, ... ,SWP_NOZORDER); // Positionsberechnungen durch "..." ersetzt
      c_Group.SetWindowPos(0, ... ,SWP_NOZORDER);
      c_Button1.SetWindowPos(0, ... ,SWP_NOZORDER);
      c_Button2.SetWindowPos(0, ... ,SWP_NOZORDER);
      InvalidateRect(... ,0); // "..." entspricht Bereich der Groupbox
      UpdateWindow();
     }
    }
    
    void CMyDialog::OnPaint()
    {
     CPaintDC dc(this);
     CMemDC memdc(&dc); // für CMemDC siehe: http://www.codeproject.com/KB/GDI/flickerfree.aspx
     Zeichenbereich.Draw(&memdc);
    }
    
    int CMyDialog::OnEraseBkgnd(CDC* pDC)
    {
     return 0; // Vorher: return CDialog::OnEraseBkgnd(pDC);
    }
    

  • Mod

    Im Parent Dialog WS_CLIPCHILDREN verwenden!

    Den WM_ERASEBKGND Handler wirf wieder raus. Das nützt nichts und der CMemDC Quatsch genauso wenig. Warum auch, es geht ja nicht um den DC des Dialoges sondern um den der Kindfenster.

    Aber wenn Du einfach mal gegooglet hättest nach:
    "flackern resize verhindern c++",
    http://www.google.de/search?hl=de&num=100&newwindow=1&q=flackern+resize+verhindern+c%2B%2B&meta=
    dann wärest Du an Stelle 2 der Suche sofort auf die Antwort in meinem Blog gestoßen:
    http://blog.m-ri.de/index.php/2008/02/05/verhindern-des-flackerns-von-controls-wenn-ein-fenster-resize-erfolgt/



  • Vielen Dank erstmal für die Antwort. - Funktioniert größtenteils.

    Aber wenn Du einfach mal gegooglet hättest nach: "flackern resize verhindern c++", dann wärest Du an Stelle 2 der Suche sofort auf die Antwort in meinem Blog gestoßen

    Sorry, ich hab gegoogelt aber irgendwie nur flackern beim Zeichnen in den Dialog gefunden.

    Den WM_ERASEBKGND Handler wirf wieder raus. Das nützt nichts und der CMemDC Quatsch genauso wenig. Warum auch, es geht ja nicht um den DC des Dialoges sondern um den der Kindfenster.

    Wenn ich das mache flackert die Zeichnung rechts wieder beim Resize. Das war ja der Grund warum ich diesen CMemDC verwendet habe.

    Wenn ich "WM_ERASEBKGND" und "CMemDC" drin lasse, dann kann ich das flackern durch "WS_CLIPCHILDREN" trotzdem reduzieren. Allerdings macht dann ein Groupbox-Control Probleme.
    Ich hab die Buttons in einer Groupbox:

    ### Groupbox ######################
    #                                 #
    #  [Button1] [Button2] [Button3]  #
    #                                 #
    ###################################
    

    Der Hintergrund der Groupbox wird einerseits nicht neu gezeichnet (Auch nicht mit InvalidateRect) und andererseits sind die Buttons darin nicht sichtbar. Wenn ich die Groupbox weglasse (oder wenn ich "WS_CLIPCHILDREN" nicht verwende) ist alles ok.
    Gibts zu den Groupboxes noch einen Trick? Hab "WS_CLIPSIBLINGS" probiert (Blog) aber das funktionierte nicht.

    [ot]Die Ursache für das flackern des HTML-Control war, dass bei jedem Resize die lokalen HTML Dateien neu gerendert wurden. 🙄 [/ot]


  • Mod

    Wie steht es mit der Z-Order der Groupbox?



  • Bis jetzt gabs keine Z-Order im Dialog. Die Reihenfolge der Controls hatte scheinbar gereicht.

    Hab jetzt:

    c_Group.SetWindowPos(0, ... ,SWP_NOZORDER);
    c_Button1.SetWindowPos(c_Group.GetWindow(GW_OWNER), ... ,0);
    

    Damit werden die Buttons korrekt angezeigt, allerdings wird der Hintergrund der Groupbox (immer noch) nicht neu gezeichnet.

    Ist "c_Group.GetWindow(GW_OWNER)" der richtige Weg? Kann man das irgendwie abkürzen / besser machen? ("&c_Group" hat irgendwie nicht funktioniert.)
    Ich nutze zum ersten mal eine Z-Order.


  • Mod

    Bei der Z-Order muss man gar nichts machen. Man erzegt enfach nur in der Reihenfolge die Childs, die man haben möchte.

    Bei einer Group-Box ist WS_CLIPSIBLINGS kontraproduktiv!



  • Bei der Z-Order muss man gar nichts machen. Man erzeugt einfach nur in der Reihenfolge die Childs, die man haben möchte.

    Naja so gings bis jetzt, aber mit "WS_CLIPSIBLINGS" war die Groupbox dann oben.

    Bei einer Group-Box ist WS_CLIPSIBLINGS kontraproduktiv!

    Was schlägst du stattdessen zum Entfernen des Flackerns vor?

    Bin geneigt die Groupbox wegzulassen.


  • Mod

    Lass doch WS_CLISIBLINGS nur für die Groupbox weg.



  • Lass doch WS_CLISIBLINGS nur für die Groupbox weg.

    Mh die Idee hatte ich auch schon, danke.

    Aber das Resultat ist das gleiche. Der Hintergrund von dem Groupcontrol wird nicht mehr neu gezeichnet.



  • Ich hab mal ein Testprojekt erstellt, wo nichts anderes drin ist als die Groupbox, die Buttons und eine kleine Zeichnung.
    Wenn du es dir mal ansehen willst. (oder jemand anderes)

    WS_CLIPCHILDREN und WS_CLIPSIBLINGS hab ich einfach in der Ressource des Dialogs geändert.

    Edit: Wenns nicht geht, dann geht es nicht. Sag was und ich verwende einfach keine Groupbox. (und stehle nicht mehr deine Zeit.)


  • Mod

    Habe den Code nur angesehen:
    1. Ich habe Dir am Anfang geschrieben, dass Du diesen CMemDC rauswerfen sollst. Inkl. Deinem OnEraseBkGnd Handler.
    2. Wie soll ein Backgroundgezeichnet werden, wenn dies keiner macht? OnEraseBkGnd macht das doch.
    3. Dein Code ist doch selbst die Ursache für das Flackern. Wieso mach Du ein Invalidate am Ende? Nur wegen Deinem Kästchen malen. Muss das sein mit dem OnPant.
    4. WS_CLIPSIBLINGS kannst Du weglassen, en die COntrols sich nicht überlappen.



  • Habe den Code nur angesehen:
    1. Ich habe Dir am Anfang geschrieben, dass Du diesen CMemDC rauswerfen sollst. Inkl. Deinem OnEraseBkGnd Handler.
    2. Wie soll ein Backgroundgezeichnet werden, wenn dies keiner macht? OnEraseBkGnd macht das doch.
    3.a Dein Code ist doch selbst die Ursache für das Flackern.
    3.b Wieso mach Du ein Invalidate am Ende? Nur wegen Deinem Kästchen malen.
    3.c Muss das sein mit dem OnPant.
    4. WS_CLIPSIBLINGS kannst Du weglassen, en die COntrols sich nicht überlappen.

    1. Ja das hast du. Dann flackert aber die Zeichnung unangenehm. Kannst du einfach testen, wenn du "useMemDC=1;" und "useMemDC=0;" testest. (Oder hast du einen anderen Vorschlag, wie ich ohne flackern zeichnen kann?)
    Dazu kommt, dass das Problem des Groupbox Hintergrundes unabhängig davon ist!

    2. Der Hintergrund wird beim CMemDC auf das Bitmap im Speicher gezeichnet, dass im Destruktor dann auf den Dialog gezeichnet wird...

    siehe Datei "memdc.h"
    FillSolidRect(m_rect,GetSysColor(COLOR_BTNFACE));
    ...
    m_pDC->BitBlt(m_rect.left, m_rect.top, m_rect.Width(), m_rect.Height(),this, m_rect.left, m_rect.top, SRCCOPY);
    

    3.a Dass dumme ist dieser Code flackert nicht. Das Problem ist der Hintergrund der Groupbox.
    3.b Ohne das Invalidate (war zu faul für ein InvalidateRect) wird die Zeichnung links nicht gezeichnet.
    3.c Wie soll ich denn sonst Zeichnen?

    4. Naja ich hatte gedacht die Groupbox überlappt mit den 2 Buttons. Und weglassen bringts auch nicht.


  • Mod

    Pack die eigene Zeichnung in ein eigenes Fenster, dass Du wie die anderen Fenster selbst neu resized.

    Dann ist dieses Fenster dafür verantwortlich was passiert. Dieses Fenster selbst benutzt dann eben einen MemDC.

    Das Problem ist, dass Du keinen MemDC vernünftig verwenden kannst, wenn das selbe Fenster auch noch Kindfenster hat.

    Dieser Konstrukt ist aber auch unsinnig. Man zerlegt in solchem Fall einfach die Cient-Fläche in entsprechende Kind-Fenster (wie ich es empfohlen habe).



  • Ok die Zeichnerei mal außen vor gelassen. Selbst dieser einfache Code funktioniert nicht:

    void CFlackerTestDlg::OnSize(UINT nType, int cx, int cy)
    {
     CDialog::OnSize(nType, cx, cy);
     if(!c_Button1.GetSafeHwnd()) return;
    
     c_Groupbox.SetWindowPos(0,cx-220,cy-70 ,200,50,SWP_NOZORDER); // Groupbox
     c_Button1 .SetWindowPos(0,cx-210,cy-50 ,80 ,20,SWP_NOZORDER); // Button 1
     c_Button2 .SetWindowPos(0,cx-110,cy-50 ,80 ,20,SWP_NOZORDER); // Button 2
     c_Button3 .SetWindowPos(0,cx-110,cy-130,80 ,20,SWP_NOZORDER); // Button Abbrechen
    
     InvalidateRect(CRect(cx-210,cy-50,cx-210+80,cy-50+20)); // Button 1
     InvalidateRect(CRect(cx-110,cy-50,cx-110+80,cy-50+20)); // Button 2
    }
    

    Siehe aktualisiertes Projekt. (einfach die FlackerTest.exe ausführen und das Fenster in der Größe ändern dann siehst du das Resultat.)

    Dieser Konstrukt ist aber auch unsinnig. Man zerlegt in solchem Fall einfach die Cient-Fläche in entsprechende Kind-Fenster (wie ich es empfohlen habe).

    Meinst du damit den Konstrukt Groupbox / Buttons? Oder die Zeichnung?


  • Mod

    Und warum invalidierst Du die Buttons? Das muss doch flackern?



  • Wenn ich das nicht mache, dann werden die Buttons hinter der Groupbox nicht angezeigt...
    Und nein, es flackert irgendwie nicht...

    Schaus dir mal an.



  • Hab direkt nach Groupbox und resize gegoogelt. Hab mitbekommen, dass die meisten ihre Flickerfreie Groupbox selber zeichnen 😮
    Hab mich entschlossen sie vorerst weg zu lassen.

    Nützlicher Link: (für andere die die Groupbox nicht einfach weglassen wollen...)
    http://www.codeguru.com/Cpp/W-D/dislog/resizabledialogs/comments.php/c1925/?

    Noch was: Danke für deine Zeit.


Anmelden zum Antworten