none
CFrameWnd invalidate??? RRS feed

  • Frage

  • Guten Tag,

    leider muß ich mit einem vermutlich trivialen Problem um Hilfe bitten. Eine DLL startet einen Thread. Dieser beinhaltet ein Fenster (CFrameWnd), in welches mittels Double Buffering ein im Memory DC gezeichnetes Bitmap angezeigt wird. Klappt einwandfrei. Mein Problem: Überdecken andere Fenster ganz oder Teilweise das Thread-Fenster, so wird dieses bei Verschieben des überlagerten Fensters nicht mehr nachgezeichnet! Ich weiß, daß ich irgendwo Invalidate() aufrufen muß, aber wo???

    Das Fenster wird mit ::PrecreateWindow() konfiguriert und mit dem folgenden Codeabschnitt geöffnet:

    BOOL CRMP_Thread::InitInstance()
    {
      size_t i = 0;
    
      CSingleDocTemplate* pDocTemplate;
    
      m_pDocManager = new CDocManager();
      pDocTemplate  = new CSingleDocTemplate(IDR_RMTHREADFRM,
    		                         RUNTIME_CLASS(CRMP_ThreadDoc),
    		                         RUNTIME_CLASS(CRMP_ThreadFrm),
    		                         RUNTIME_CLASS(CRMP_ThreadView));
    
      m_pDocManager->AddDocTemplate(pDocTemplate);
      m_pDocManager->OnFileNew();
    
      // die übergebenen Originaldaten werden
      // in das aktive Dokument kopiert
      POSITION Pos = pDocTemplate->GetFirstDocPosition();
      dynamic_cast<CRMP_ThreadDoc*>(pDocTemplate->GetNextDoc(Pos))->SetPlotData(m_pBuffer,
                                                                                m_pScl,
                                                                                m_pTtl);
      // Verbindung zu den Originaldaten kappen!
      m_pScl = NULL;
      m_pTtl = NULL;
      for (i = 0; i < m_pPlotBuffer->size(); i++)
        m_pPlotBuffer->at(i)->vValTable.clear();
    
      m_pPlotBuffer->clear();
    
      if (m_pMainWnd) // Ab hier wird das Thread-Fenster geöffnet
      {
        CString Str;
        Str.AppendFormat(_T("Canvas Window of Thread #%d"), m_iThreadNo);
        ((CRMP_ThreadFrm*)m_pMainWnd)->SetWindowText(Str);
      }
      else
        return FALSE;
    
      return TRUE;
    }

    Dank im Voraus für Hilfe


    M. Thaddaeus


    Freitag, 30. Mai 2014 10:13

Antworten

  • Guten Tag Herr Richter,

    nun habe ich Ihren Vorschlag umgesetzt, habe die erste Clipping Region herausgeschmissen und sichere den DC mittels

    n = SaveDC(pDC->mhDC);

    bevor ich irgendetwas anderes anstelle. Anschließend werde die gesamten Zeichenarbeiten auf dem Memory DC augeführt und unmittelbar bevor ich das fertige DIB vom Memory DC auf den Fenster DC kopiere rufe ich die Funktion

    RestoreDC(pDC->m_hDC, n);

    auf. Wie vorausgesagt funktioniert nun alles bestens.

    Nochmals herzlichen Dank für Ihre Mühe und Geduld.

    K. H. Renders.


    M. Thaddaeus

    • Als Antwort markiert m-thaddaeus Freitag, 20. Juni 2014 09:43
    Donnerstag, 12. Juni 2014 09:29

Alle Antworten

  • Wenn Dein Fenster nur in OnPaint zeichnet ist alles gut.

    Windows sorgt grundsätzlich selbst dafür, dass ein Fenster das freigelegt wird eine neues WM_PAINT Nachricht erhält.

    Du must in dem geschilderten Fall nichts machen.


    Martin Richter -- MVP for VC++ [Germany] -- http://blog.m-ri.de

    Montag, 2. Juni 2014 08:01
    Moderator
  • Wenn Dein Fenster nur in OnPaint zeichnet ist alles gut.

    Windows sorgt grundsätzlich selbst dafür, dass ein Fenster das freigelegt wird eine neues WM_PAINT Nachricht erhält.

    Du must in dem geschilderten Fall nichts machen.


    Martin Richter -- MVP for VC++ [Germany] -- http://blog.m-ri.de

    Guten Tag Herr Richter,

    für Ihre schnelle Antwort danke ich Ihnen. Gezeichnet wird in meinem Code in der AFX-Funktion OnDraw(CDC* pDC);. Um die Flackerei zu vermeiden verwende ich einen MemoryDC (CRMemoryDC* pMDC = new CRMemoryDC(pDC, &ClientRect); wie von Keith Rule vorgeschlagen). Bei allen Aufrufen meiner Unterfunktionen zum Zeichnen der einzelnen Elemente wird eben dieser MemoryDC (pMDC) mit übergeben. Es werden insgesamt 11 Unterfunktionen aufgerufen, immer aus OnDraw() heraus. Eine kleinere Teilfläche wird vor dem letzten Zeichenaufruf zu Clip Region deklariert und in dieser gezeichnet. Nach Ende der kompletten Zeichnerei wird diese Clip Region wieder gelöscht.

    Nach der kompletten Zeichnung des DIB auf dem MemoryDC und dem Löschen der Clip Region wird mit delete pMDC das fertige DIB mittels StrchBLT() auf den Fenster-DC übertragen.

    Soweit klappt das alles wirklich einwandfrei! Wird aber das Fenster durch ein anderes überlappt und möglicherweise sogar verschoben, so bleiben eben die Spuren des überlappenden Fensters erhalten (leider). Es sind aber unverständlicherweise keinerlei Spuren in der Clip Region des Fensters festzustellen.

    Für mich ist das ganze ziemlich rätselhaft. In meinem Falle hustet mir mein Windows (7 x64) etwas und verrät mir leider nicht warum. Können Sie damit etwas anfangen?

    Mit freundlichen Güßen
         K. H. Renders

    ---------------------------------------------------------------------------------------------------------------------------------
    Dipl.-Ing. Klaus Helmut Renders
    Ahornweg 8
    34314 Espenau
    ---------------------------------------------------------------------------------------------------------------------------------


    M. Thaddaeus

    Montag, 2. Juni 2014 11:40
  • Ich verstehe die Aussage mit der ClipRegio nicht ganz.

    Kann es sein, dass nicht die alte ClipRegion restauriert wird? Die gesetzte Clipping Region darf nicht verändert werden. Bzw. mussnach Ausführen von WM_PAINT wieder gesetzt sein.


    Martin Richter -- MVP for VC++ [Germany] -- http://blog.m-ri.de

    Mittwoch, 4. Juni 2014 10:16
    Moderator
  • Guten Tag Herr Richter,

    leider habe ich mich da wohl etwas unglücklich ausgedrückt. Ich wollte Sie mit meinem Quellcode an sich nicht belästigen. Aber zum besseren Verständnis muß, es wohl sein. Hier der (unwesentlich) gekürzte Quellcode meiner OnDraw() Funktion:

    void CRMP_ThreadView::OnDraw(CDC* pDC)
    {
      int TW = 0;
      CRect ClientRect, DRect;
      CRgn ClipRgn;
    
      if (pDC->IsPrinting())
        ...
        ...
    
      GetClientRect(ClientRect);
      SetBkgnd(pDC, ClientRect);
    
      CRMP_ThreadDoc* pDoc = dynamic_cast<CRMP_ThreadDoc*>(GetDocument());
    
      // double buffering
      CRMemoryDC* pMDC = new CRMemoryDC(pDC, &ClientRect);
    
      // dünnen Rahmen um die Klient Area zeichnen
      DrawFrameRect(pMDC, ClientRect);  
      // Rechteckrahmen um die Plotfläche zeichnen
      DrawPlotRect(pMDC);               
    
      // Gridlinien innerhalb der Plotfläche zeichnen
      DrawGrid(pMDC);
    
      // Beschriftungsstriche an die
      // äußere Plotfläche zeichnen
      DrawTicks(pMDC);
    
      // Nullkoordinaten zeichnen, sofern
      // diese auf der Plotfläche liegen.
      DrawZeroCoordinates(pMDC);
    
      // Beschriftungen anbringen
      LabelGrid(pMDC, TW);
      LabelAxes(pMDC, TW);
      LabelTitle(pMDC, pDoc->m_tPlotTitle);
    
      // Clipping Region für Plotfläche auf dem
      // Memory-DC erzeugen.
      // Verhindert daß die Funktionskurve über
      // die Plotfläche hinaus gezeichnet werden
      // kann.
      pMDC->LPtoDP(&(pDoc->m_rPlotRect));
      ClipRgn.CreateRectRgnIndirect(&(pDoc->m_rPlotRect));
      pMDC->SelectClipRgn(&ClipRgn);
    
      // Fuktionskurve zeichnen
      FunctPlot(pMDC);
    
      // Anfangszustand des Memory-DC
      // wieder herstellen
      ClipRgn.DeleteObject();
    
      // DIB des Memory-DC auf den DC des Fensters
      // kopieren und Memory-DC löschen
      pMDC->DPtoLP(&(pDoc->m_rPlotRect));
      delete pMDC;
    }
    

    Meine CRgn wird nur auf dem Memory-DC in der Größe der Plotfläche angelegt und nach dem Zeichnen der Funktionskurven wieder entfernt, noch bevor das DIB vom der Memory-DC auf den Fenster-DC "geblittet" (saublödes Wort!) wird.

    Wie ich schon andeutete, weist diese Plotfläche keinerlei Überlappungsspuren auf, im Gegensatz zur übrigen Fensterfläche.

    Zu Ihrer weiteren Information führe ich noch den Destructor der Memory-DC Funktion hinzu:

      ~CRMemoryDC()	
      {		
        if (m_bMemDC) 
        {
          m_pDC->StretchBlt(m_DRect.left,
                            m_DRect.top,
                            m_DRect.Width(),
                            m_DRect.Height(),
                            this,
                            m_SRect.left,
                            m_SRect.top,
                            m_SRect.Width(), 
                            m_SRect.Height(), 
                            SRCCOPY);
    
    			SelectObject(m_oldBitmap);
          m_bitmap.DeleteObject();
        } 
        else 
        {
          ...
          ...
        }	
      }
    

    Vielleicht fällt Ihnen dazu etwas ein, ich jedenfalls stehe nach wie vor wie der Ochs vor dem Berge.

    Mit freundlichen Grüßen

    K. H. Renders


    M. Thaddaeus

    Freitag, 6. Juni 2014 08:05
  • Das kann doch nicht funktionieren.

    Es ist genau wie ich gesagt habe. Die Clipping Region ist das Problem.

    Du änderst die Clipping Region aber Du stellst nicht den alten Zustand wieder her.
    Nachdem Aufruf Deiner Funktion bleibt Deine Clipping Region bestehen.
    Wieso glaubst Du, dass die Clipping Rehion zurück gesetzt wird, wenn Du das Objekt löscht.Es steht klar in der MSDN, dass durch SelectClipRgn eine Kopie verwendet wird.

    Wenn, dann musst Du die alte ClipGrn laden (GetClipRgn), diese sichern, und bei Bedarf (es könnte ja keine gegeben haben) diese zurücksetzen.

    Oder benutze SaveDC RestoreDC.


    Martin Richter -- MVP for VC++ [Germany] -- http://blog.m-ri.de

    Dienstag, 10. Juni 2014 10:35
    Moderator
  • Guten Tag Herr Richter,

    es geschehen noch Zeichen und Wunder, denn ich habe es tatsächlich geschafft!!! Allerdings nicht mit der von Ihnen vorgeschlagenen - sicherlich besseren - Methode, sondern eigentlich ganz primitiv.

    Ich hatte - wie ich Ihnen berichtete - beobachtet, daß meine Clip Region der Plotfläche durch die Überlappungen nicht verunstaltet wurde, sondern nur deren Umgebung, also der nicht "geclippten" Fensterfläche. Daraufhin startete ich den Versuch, zunächst die gesamte Fensterfläche zu "clippen", dieses Objekt wieder zu löschen, dann die Plotfläche zu "clippen" und danach dieses Objekt ebenfalls wieder zu löschen.

    Hier der gekürzte Code:

    (Ich habe das gesamte schmückende Beiwerk weggelöscht um das ganze etwas lesbarer zu machen.)

    void CRMP_ThreadView::OnDraw(CDC* pDC)
    {
      ...
      ...
      ...
    
      // double buffering
      CRMemoryDC* pMDC = new CRMemoryDC(pDC, &ClientRect);
    
      // Clipping Region für gesamte Fensterfläche
      // auf dem Memory-DC erzeugen.
      pMDC->LPtoDP(&ClientRect);
      ClipFRgn.CreateRectRgnIndirect(&ClientRect);
      pMDC->SelectClipRgn(&ClipFRgn);
    
      // dünnen Rahmen um die Client Area zeichnen
      // Rechteckrahmen um die Plotfläche zeichnen
      // Gridlinien innerhalb der Plotfläche zeichnen
      // Beschriftungsstriche außerhalb an die Plotfläche zeichnen
      // Nullkoordinaten zeichnen sofern Null auf der Plotfläche liegt.
      // Beschriftungen anbringen
    
      // Clipping Region der Fensterfläche löschen und
      // Anfangszustand des Memory-DC wieder herstellen
      ClipFRgn.DeleteObject();
    
      // Clipping Region für Plotfläche auf dem
      // Memory-DC erzeugen.
      // Verhindert daß die Funktionskurve über
      // die Plotfläche hinaus gezeichnet werden
      // kann.
      pMDC->LPtoDP(&(pDoc->m_rPlotRect));
      ClipPRgn.CreateRectRgnIndirect(&(pDoc->m_rPlotRect));
      pMDC->SelectClipRgn(&ClipPRgn);
    
      // Fuktionskurve zeichnen
      FunctPlot(pMDC);
    
      // Clipping Region der Plotfläche löschen und
      // Anfangszustand des Memory-DC wieder herstellen
      ClipPRgn.DeleteObject();
    
      // DIB des Memory-DC auf den DC des Fensters
      // kopieren und Memory-DC löschen
      pMDC->DPtoLP(&(pDoc->m_rPlotRect));
      delete pMDC;
    
      ...
      ...
    }

    Sie werden es nicht glauben, aber das funktioniert tatsächlich! Ich vermute allerdings, daß die Puristen über die Lösung den Kopf schütteln werden. Das ist mir das momentan egal. Es funktioniert und ich komme weiter.

    Ihnen, Herr Richter, bin ich sehr dankbar für Ihre Hilfe. Natürlich werde ich Ihren Vorschlag mittels SaveDC und RestoreDC einmal ausprobieren. Momentan aber habe ich aber mit CTabCtrl zu kämpfen.

    Mit dankbaren Grüßen

      K. H. Renders.



    M. Thaddaeus

    Mittwoch, 11. Juni 2014 12:50
  • Ich kann von Ihrem Verfahren nur abraten.

    Der DC sollte nicht verändert werden und Sie verändern den DC durch diese Operation. Zudem ware SaveDC und RestoreDC weitaus einfacher.


    Martin Richter -- MVP for VC++ [Germany] -- http://blog.m-ri.de

    Mittwoch, 11. Juni 2014 13:24
    Moderator
  • Guten Tag Herr Richter,

    nun habe ich Ihren Vorschlag umgesetzt, habe die erste Clipping Region herausgeschmissen und sichere den DC mittels

    n = SaveDC(pDC->mhDC);

    bevor ich irgendetwas anderes anstelle. Anschließend werde die gesamten Zeichenarbeiten auf dem Memory DC augeführt und unmittelbar bevor ich das fertige DIB vom Memory DC auf den Fenster DC kopiere rufe ich die Funktion

    RestoreDC(pDC->m_hDC, n);

    auf. Wie vorausgesagt funktioniert nun alles bestens.

    Nochmals herzlichen Dank für Ihre Mühe und Geduld.

    K. H. Renders.


    M. Thaddaeus

    • Als Antwort markiert m-thaddaeus Freitag, 20. Juni 2014 09:43
    Donnerstag, 12. Juni 2014 09:29