Fenster wirklich neu zeichnen
-
Hallo zusammen,
Das Haupt-Fenster meiner Anwendung besteht aus Zeilen von Rechtecken.
Je nach Einstellung werden in manchen dieser Zeilen mehr oder weniger Infos untergebracht.
Somit ändert sich bei Einstellungsänderung auch die Höhe dieser Zeilen.Leider überlagern sich nun alte und neue Darstellung, obwohl beim Rücksprung aus den Einstellungen,
über mehrere Fensterebenen, das besagte Hauptfenster zunächst leer ist.
Es wird immer die alte und die neue Darstellung übereinander gezeichnet.Meine erste Idee war, vor dem Neuaufbau, am Anfang meiner "Draw()"-Funktion, ein "InvalidateRect(&rect)" aufzurufen.
Es hat sogar zunächst geklappt, und ich war happy.Nun passiert plötzlich nichts mehr!
Ich habe es nun am Anfang der "Draw()" und am Ende versucht.
ich habe, neben dem "InvalidateRect(&rect)" auch "RedrawWindow()" und "UpdateWindow()" eingesetzt, sogar alles zusammen.
Keine Änderung.Aber ich muss nur das Fenster etwas grösser oder kleiner machen, dann erscheint das Fenster korrekt.
Auch ein kurzes Ziehen am Scrollbalken hilft.Was ist da passiert und was mache ich falsch?
Vielen Dank.
-
Ohne Code sind da sinnvolle Ratschläge schwierig.
-
@DocShoe
Ja, schwierig.
Aber ich versuchs mal codemässig zusammenzufassen.void CViewTitanCe::OnDraw(CDC* pDC) { ... HeapCompact(GetProcessHeap(),0);// 2 Anweisungen stammen noch von menem Vorgänger UpdateCmdUI(); switch(m_ScreenDsc.sScreenDsc[m_ScreenDsc.wNoOf].m_WelchesFenster) { case DRAWMAINVIEW: DrawMainView(pDC); // der View, um den es sich handelt break; ... HeapCompact(GetProcessHeap(),0);// auch vom Vorgänger } void CViewTitanCe::DrawMipasView(CDC* pDC) { RECT sRect,wRect; BOOL bThreeLines=FALSE; ... if(a)// Falls sich Einstellung ändert, erfolgt Umstellung der Anzeige --> Darstellungsprobleme! bThreeLines = TRUE;// bestimmt Höhe einer Recheck-Zeile, gemäss Einstellung else bThreeLines = FALSE; ... pOldBrush = pDC->SelectObject(&brush); pDC->SetBkMode(OPAQUE); pDC->SetBkColor(g_TestColorBkStd); pDC->SetTextColor(g_TestColorTextStd); GetClientRect(&wRect); if (g_bViewSettingChange) {// globale Var, die über Einstellungs-Dialog gesetzt wird // Habe das hier am Anfang und auch am Ende dieser Methode eingesetzt g_bViewSettingChange = FALSE; InvalidateRect(&wRect);// Mit beliebigen Kombinationen aus den drei Fktn's versucht, anfangs sogar erfolgreich mit "Invalidate.." RedrawWindow(); UpdateWindow(); } ... // Hier wird dann gezeichnet }
-
Die
OnDraw
-Methode (bzw. intern die Reaktion auf diePAINT
-Message) darf nur reine Zeichenfunktionen enthalten, keine sonstigen Aktualisierungen oder ähnliches.
Diese Aktualisierungen mußt du in deiner UI-Logik durchführen (also was du als "Rücksprung aus den Einstellungen" bezeichnet hast).
Diese Aktualisierungsfunktionen (wieInvalidate()
,InvalidateRect(...)
,RedrawWindow()
, etc.) versenden intern einfach diePAINT
-Message (und wenn dies nicht intern von der Windows-Messagequeue abgefangen wäre, würde es, wie du es jetzt gemacht hast, zu einer Endlosschleife kommen).
-
Kann da erst mal nix Merkwürdiges sehen, außer dass beim Zeichnen auf Einstellungsänderungen reagiert wird. Ungewöhnlich, sollte in der Konstellation aber ok sein.
- Wo kommt denn
brush
fürpDC->SelectObject( &brush )
her? Wird der Original-Brush am Ende wiederhergestellt? - Kannst du das Zeichnen eurer Zeilen komplett auskommentieren und einfach mal Dummy Elemente zeichnen?
- Wo kommt denn
-
@Th69
Hab ich auch erst gedacht, aber das passiert nur, wenng_bViewSettingChange
true ist. In dem Fall wird`s auf false gesetzt und das Neuzeichnen getriggert. Prinzipiell hast du aber recht, das sollte an anderer Stelle passieren.
-
@Th69
Hier noch de eine Fktn von meinem Vorgänger:void UpdateCmdUI(BOOL bFromThread) { if(bFromThread) // 20100520 RH theApp.m_pFrame->SendMessage(WM_COMMAND,ID_UPDATE_TOOLBAR); // else // theApp.m_pFrame->m_wndCommandBar.OnUpdateCmdUI(theApp.m_pFrame,TRUE); }
-
Wie ich schon geschrieben habe, kommentier zur Fehlersuche mal alles aus und nach- und nach wieder ein. Oder lass alles auskommentiert und zeichne ein paar Elemente selbst um zu gucken, ob das funktioniert.
-
@DocShoe
Die "brushes", etc, sind lokal definiert.
Am Ende der "DrawMainView(..)" erfolgt:if(pOldFont) pDC->SelectObject(pOldFont); delete pCompatibleDC; if(pOldBrush) pDC->SelectObject(pOldBrush); if(CheckSprache(pAnlage,4)) CreateStdFont(FALSE);
-
@DocShoe
Zwischen meinem ersten Versuch, der funktionierte, habe ich nichts an dem Teil des Quellcodes geändert.
Aber ich versuche auch mal das auskommentieren und auch die alte Version, die funktionierte.
-
@elmut19
Ja wenn nix geändert worden ist müssen es ja Neutrinoeinschläge in der CPU oder GPU sein.
-
@DocShoe
oder Windows Update ...
... Typische Aussage, ich weiss auch: "Wir haben nix geändert ..."
Klingt schon lustig.Aber eine ältere Version (leider nicht die, die erste Implementierung dazu hatte) funktioniert auch nicht mehr.
-
Lass doch mal die KI drüber schauen. In Syntax-Fragen ist sie gut.
-
- HeapCompact hat nicht im OnDraw zu tun.
- OnDraw wird im Kontext von einer WM_PAINT Nachricht versendet.
Entsprechend wird diese Nachricht nur erzeugt wenn:
a. das Fenster/der Auschnitt sichtbar ist
b. der Ausschnitt auch aktualisiert werden muss - In OnDraw hat ein UpdateWindow/RedrawWindow/InvalidateRect nichts zu suchen. Denn dies führt evtl. zu rekursiven aufrufen.
- In einem OnDraw musst Du alle Bereiche auch neu zeichnen die betroffen sind.
- Nach dem OnDraw / WM_PAINT werden alle Fenster/Bereiche validiert, das heißt sie bekommen das Flag "aktuell"
- Durch ein InvalidateRect bekommt ein Bereich/Fenster das Flag "nicht aktuell"
- Ein UpdateWidnow/RedrawWindow wird alle Bereiche aktualisieren, die als "nicht aktuell" markiert sind.
-
@Erhard-Henkes
Hatte ich auch schon mal gemacht (anderer Fall)
Hat erkannt, was ich tun wollte. Aber nicht den Fehler gefunden.
-
@elmut19 sagte in Fenster wirklich neu zeichnen:
oder Windows Update ...
Bitte bleibt doch mal sachlich. Das Problem ist die WinAPI mit ihren vielen Seiteneffekten. Erst neulich hat Dr. Memory z.B. bei mir einen Fehler im Subclassing entdeckt. Und so wie es aussieht, verursachte dieser Fehler nur in 0,001% aller Fälle einen Absturz.
Ein paar weitere Punkte:
- DIe Anweisung
delete pCompatibleDC;
finde ich etwas merkwürdig und sollte man überprüfen. - Ferner würde ich den folgenden Code prüfen:
if(bFromThread) // 20100520 RH theApp.m_pFrame->SendMessage(WM_COMMAND,ID_UPDATE_TOOLBAR);
Wenn der Aufruf hier durch einen Thread erfolgte, so sendest du eine
WM_COMMAND
Nachricht anm_pFrame
. Oder genauer gesagt, fügst du eineWM_COMMAND
Nachricht in die Message Queue vonm_pFrame
ein und wartest bis diese bearbeitet wurde.-> Was passiert wenn du stattdessen
PostMessage()
WM_COMMAND` aufrufst?- BTW: EIn Klassiker beim Threading sind Synchronisationsfehler. Welche Thread Safety benötigst du? Und wie Thread Safe ist dein Code?
- DIe Anweisung
-
Lass da generell mal cppcheck drüberlaufen.
-
@Martin-Richter
Tja, wo kann ich das "Invalidate.."-Zeugs dann hintun?
Meine "DrawMainView(..)" ist ja praktisch Teil der "OnDraw()".
Und dort wird eigentlich alles neu gezeichnet.
Aber nicht nur das Neue, sondern auch das Alte. Scheint zumindest so.
Andererseits werden in dem View zyklisch Texte neu ausgegeben, die sich ändern.
Diese werden nicht übereinander geschrieben.Das mit "rekursive Aufrufe" habe ich ja erfolgreich unterbunden.
Was mein Vorgänger mit seinem "Heap"-Zeugs vorhatte, weiss ich nicht.
Es hat seit 20? Jahren nicht gestört.
-
Invalidate brauchst Du gar nicht. Außer Du willst das neu Zeichnen erzwingen.
Wenn Dinge übereinander gezeichnet werden, dann wird evlt. der Hintergrund nicht neu gezeichnet. Also das alte Zeugs überschrieben.
Du musst nichts "unterbinden", sondern es unterlassen.
Wenn es nicht stört heißt es nicht dass es richtig ist.
-
@elmut19 sagte in Fenster wirklich neu zeichnen:
@Martin-Richter
Tja, wo kann ich das "Invalidate.."-Zeugs dann hintun?An die Stelle, an der die Konfiguration geändert worden ist. Am Besten entkoppelst du das auch noch und löst das per Observer Pattern:
(Pseudocode) void SomeClass::set_configuration( MyConfigurationData const& cfg ) { // Beim Setzen einer neuen Konfiguration wird ein Ereignis ausgelöst, das an alle angemeldeten Subscriber abgesetzt wird. Configuration_ = cfg; OnConfigurationChanged.fire( ); }
CViewTitanCe.cpp CViewTitanCe::CViewTitanCe() { // Subscriber anmelden instance_of_some_class().OnConfigurationChanged.subscribe( OnConfigurationChanged ); } CViewTitanCe::~CViewTitanCe() { // Subscriber abmelden instance_of_some_class().OnConfigurationChanged.unsubscribe( OnConfigurationChanged ); } void CViewTitanCe::OnConfigurationChanged() { // Callback für Konfigurationsänderung: Neuzeichnen anstossen Invalidate(); }
Leider gibt's im Standard C++ keine Publisher/Subscriber Unterstützung, da musst du dir was eigenes basteln. Für den Anfang reicht da vielleicht auch schon ein Funktionszeiger oder
std::function
.