Fensterstil und -größe während der Laufzeit ändern



  • Hallo,

    ich möchte während des laufenden Programms den Fensterstil und die Fenstergröße ändern. Dazu setze ich zuerst den Stil mit SetWindowLongPtr() und rufe anschließend SetWindowPos() mit der neuen Fenstergröße sowie dem Parameter SWP_FRAMECHANGED auf, damit die Änderungen umgesetzt werden. Der Wechsel erfolgt hier probeweise durch linke und rechte Maustaste:

    case WM_LBUTTONUP:
        SetWindowLongPtr(hWnd_, GWL_STYLE, WS_POPUP);
        SetWindowPos(hWnd_, HWND_TOP, 100, 100, 320, 240, SWP_FRAMECHANGED | SWP_SHOWWINDOW);
        break;
    
    case WM_RBUTTONUP:
        SetWindowLongPtr(hWnd_, GWL_STYLE, WS_OVERLAPPEDWINDOW & ~(WS_THICKFRAME | WS_MAXIMIZEBOX));
        SetWindowPos(hWnd_, HWND_TOP, 100, 100, 640, 480, SWP_FRAMECHANGED | SWP_SHOWWINDOW);
        break;
    

    Im Prinzip funktioniert das, denn das Fenster erscheint in der neuen Größe und auch mit dem neuen Stil, allerdings bleibt zusätzlich die alte Darstellung des Fensters sichtbar, bis es z.B. durch Drüberschieben eines anderen Fensters gelöscht wird (d.h. das, was hinter dem alten Fenster liegt, wird nicht neu gezeichnet, um die alte Version des Fenster zu überdecken).

    Könnt ihr mir bei diesem Problem helfen?

    ph4nt0m



  • Was ist von der alten Darstellung noch sichtbar?
    Der Fensterrahmen (also den sog. Nonclient-Bereich) und/oder den Fensterinhalt (also den sog. Client-Bereich)?

    Vielleicht löst UpdateWindow() nach SetWindowPos() Dein Problem?

    Alternativ kannst Du auch mit InvalidateRect() probieren, aber das wirkt sich glaub ich nur auf den Client-Bereich des Fensters aus.

    HTH,
    Martin



  • Also wenn ich auf das WS_POPUP wechsle, ist dahinter erst einmal noch das gesamte Fenster (also auch Titelleiste und Rahmen) zu sehen. Wenn ich über diesen "Rest" z.B. ein Notepad-Fenster schiebe, verschwindet diese alte Darstellung (weil sie dann einfach durch den Editor überzeichnet wird), es ist also kein eigentliches Fenster, sondern nur noch ein Überrest "im Grafikspeicher", der beim Wechsel offenbar nicht entfernt wird.

    Leider löst weder UpdateWindow(), noch InvalidateRect() das Problem. Trotzdem danke ich dir schon einmal für deine Mühe 🙂



  • Du musst das Ganze in eine eigene Funktion packen und dann alle Flags setzen oder löschen.
    Beispiel:

    void ToggleWndStyle(HWND hwnd)
    {
    	LONG_PTR dw = GetWindowLongPtr(hwnd,GWL_STYLE);
    	if(dw & WS_POPUP)
    	{
    		dw &= ~WS_POPUP;
    		dw |= WS_OVERLAPPEDWINDOW|WS_THICKFRAME;
    	}
    	else
    	{
    		dw &= ~(WS_OVERLAPPEDWINDOW|WS_THICKFRAME);
    		dw |= WS_POPUP;
    	}
    	SetWindowLongPtr(hwnd, GWL_STYLE, dw);
    	SetWindowPos(hwnd, 0,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE|SWP_FRAMECHANGED|SWP_DRAWFRAME|SWP_NOZORDER);
    }
    

    So sollte das eigentlich funktionieren.



  • Das führt leider genau zum selben Ergebnis. Wenn ich erst den aktuellen Stil abrufe, daraus dann alles per AND lösche und anschließend mit dem neuen Stil per OR verknüpfe, ist das ja auch gleichbedeutend damit, dass ich den neuen Stil einfach direkt zuweise. Und genau das habe ich ja bisher immer gemacht.


  • Mod

    Ich bin mir nicht sicher ob dies erlaubt ist den Stil so zu wechseln.
    Als Trick würde ich Dir einfach raten das Fenster zu verstecken, den Stil zu ändern und das Fenster wieder zu erzeugen. Damit müssten die Artifakte verschwinden.



  • Vielen Dank, ein ShowWindow(hWnd_, false); zu allererst sorgt tatsächlich dafür, dass die Grafikreste verschwinden 👍

    In der MSDN konnte ich leider keine Einschränkung bzgl. des Wechselns von Fensterstilen finden und bin daher davon ausgegangen, dass das gehen muss. Was genau meinst du denn mit "...ob dies erlaubt ist den Stil so zu wechseln"? Vermutest du, dass kleinere Änderungen keine Probleme machen, in diesem Fall aber der krasse Wechsel zwischen WS_OVERLAPPEDWINDOW und WS_POPUP nicht ohne Nebenwirkungen möglich ist?


  • Mod

    Das Ändern der Stile WS_CHILD, WS_OVERLAPPED und WS_POPUP ist massiv. Aber wenn es geht ist es eigentlich erlaubt. Manche Stile lassen sich nicht mehr ändern nach dem Anlegen des Fensters.

    Wenn das Hide/Show Dein Problem löst würde ich das einfach so einbauen.
    Optisch dürfte das kaum auffallen.



  • Naja, das Fenster flackert vielleicht einmal kurz und in der Taskleiste sieht man natürlich auch das Fenster erst verschwindet und anschließend wieder auftaucht, aber da es hier um einen Wechsel von Fenster- auf Vollbildmodus bei einer Direct3D-Anwendung geht, sollte das kein so großes Problem sein 🙂



  • Unter welcher Version arbeitet ihr? Bei mir macht oben geschriebener Vorschlag unter XP SP 3 keinerlei Probleme. Unter Win 7 habe ich es heute morgen nicht getestet.



  • Hier läuft ebenfalls XP mit SP3. Allerdings veränderst du in deinem Beispiel auch ausschließlich den Stil des Fensters, nicht jedoch Größe oder Position. Somit wird die neue Variante des Fensters natürlich exakt über die alte gezeichnet und man kann von letzterer dann natürlich auch keine Reste mehr sehen, da diese ja vollständig überzeichnet werden.

    ~Im Übrigen muss ich in deinem Beispiel auch noch ein SWP_SHOWWINDOW zu den Flags anfügen (oder wahlweise anschließend ShowWindow(hwnd, SW_SHOW); aufrufen), um das "neue" Fenster überhaupt sehen zu können.~



  • Ich habe gerade nachgeschaut, wie ich es einmal mit meinem MDI-Client gemacht habe, dort ist auch erst einmal ein Aufruf von ShowWindow(hwnd,SW_HIDE) zu finden. Scheint wirklich die einzige Möglichkeit zu sein, damals habe ich _einige_ Zeit damit verbracht, eine Lösung zu finden. Bei Child-Fenstern fällt es natürlich nicht so auf.



  • Gerade habe ich noch einmal etwas rumgespielt, und, siehe da...

    void ToggleWndStyle(HWND hwnd, const RECT* prcSWNormal)
    {
    	LONG_PTR dw = GetWindowLongPtr(hwnd,GWL_STYLE);
    	RECT rc;
    	if(dw & WS_POPUP)
    	{
    		dw &= ~(WS_POPUP|WS_MAXIMIZE|WS_THICKFRAME);
    		dw |= WS_OVERLAPPEDWINDOW;
    		rc = *prcSWNormal;
    	}
    	else
    	{
    		dw &= ~(WS_OVERLAPPEDWINDOW);
    		dw |= (WS_POPUP|WS_MAXIMIZE|WS_THICKFRAME);
    		SetRect(&rc,0,0,GetSystemMetrics(SM_CXSCREEN),GetSystemMetrics(SM_CYSCREEN));
    		SetMenu(hwnd,NULL);
    	}
    	SetWindowLongPtr(hwnd, GWL_STYLE, dw);
    	SetWindowPos(hwnd, HWND_TOP,rc.left,rc.top,rc.right-rc.left,rc.bottom-rc.top,SWP_FRAMECHANGED|SWP_DRAWFRAME);
    }
    

    ...läuft.
    prcSWNormal wird natürlich nur benötigt bei einem Wechsel WS_POPUP->WS_OVERLAPPEDWINDOW



  • Ich weiß wirklich nicht, weshalb bei dir keine Reste übrig bleiben. Wenn ich es bei mir so versuche, tritt immer das beschriebene Problem auf. Könntest du vielleicht die Größenänderung so lassen, jedoch die Styles einfach mal ohne diese vielen Bitoperationen setzen? So stelle ich mir den Code dann etwa vor:

    void ToggleWndStyle(HWND hwnd, const RECT* prcSWNormal)
    {
        LONG_PTR dw = GetWindowLongPtr(hwnd,GWL_STYLE);
        RECT rc;
        if(dw & WS_POPUP)
        {
            dw = WS_OVERLAPPEDWINDOW & ~(WS_THICKFRAME | WS_MAXIMIZEBOX);
            rc = *prcSWNormal;
        }
        else
        {
            dw = WS_POPUP;
            SetRect(&rc,0,0,GetSystemMetrics(SM_CXSCREEN),GetSystemMetrics(SM_CYSCREEN));
            SetMenu(hwnd,NULL);
        }
        SetWindowLongPtr(hwnd, GWL_STYLE, dw);
        SetWindowPos(hwnd, HWND_TOP,rc.left,rc.top,rc.right-rc.left,rc.bottom-rc.top,SWP_FRAMECHANGED|SWP_DRAWFRAME);
    }
    


  • Hast du den Code 1:1 kopiert und ausgeführt? Das Problem ist evtl., dass du eine 3D - Vollbildanwendung wiederherstellen und anschließend maximieren möchtest.
    Zu den Bitoperationen:
    Bei einer Zuweisung an dw (dw=...) löschst du ja auch bestehende Styles (WS_VISIBLE , WS_BORDER etc.). Dies wird eben dadurch verhindert.
    Statt dw = dw | WS_POPUP kannst du eben dw|=WS_POPUP schreiben. Ist viel kürzer und man gewöhnt sich sehr schnell daran.
    Probier das Ganze mal mit einem normalen Fenster aus. Hast du dann immer noch Probleme?



  • Das mit den Kurzformen der Operatoren usw. ist mir schon klar 😉 Auch, dass du durch die Bitoperationen andere gesetzt Flags behalten wolltest. Ich bin aber davon ausgegangen, dass gar keine weiteren Flags gesetzt sind und somit eben auch eine einfache Zuweisung reicht. Das kommt natürlich darauf an, mit welchen Flags du dein Fenster zu Beginn erstellt hast. Ich habe z.B. CreateWindowEx() die Flags WS_OVERLAPPEDWINDOW & ~(WS_THICKFRAME | WS_MAXIMIZEBOX) übergeben und möchte eben genau zwischen diesem verknüpften Stil und WS_POPUP wechseln, daher wäre bei mir dann auch eine reine Zuweisung ausreichend.

    Was aber viel wichtiger ist: Du hast mein Problem gelöst 😃 Ich habe aus diesem Code:

    case WM_LBUTTONUP:
        SetWindowLongPtr(hWnd_, GWL_STYLE, WS_POPUP);
        SetWindowPos(hWnd_, HWND_TOP, 100, 100, 320, 240, SWP_FRAMECHANGED | SWP_SHOWWINDOW);
        break;
    
    case WM_RBUTTONUP:
        SetWindowLongPtr(hWnd_, GWL_STYLE, WS_OVERLAPPEDWINDOW & ~(WS_THICKFRAME | WS_MAXIMIZEBOX));
        SetWindowPos(hWnd_, HWND_TOP, 100, 100, 640, 480, SWP_FRAMECHANGED | SWP_SHOWWINDOW);
        break;
    

    diesen hier gemacht:

    case WM_LBUTTONUP:
        SetWindowLongPtr(hWnd_, GWL_STYLE, WS_VISIBLE | WS_POPUP);
        SetWindowPos(hWnd_, HWND_TOP, 100, 100, 320, 240, SWP_FRAMECHANGED);
        break;
    
    case WM_RBUTTONUP:
        SetWindowLongPtr(hWnd_, GWL_STYLE, WS_VISIBLE | WS_OVERLAPPEDWINDOW & ~(WS_THICKFRAME | WS_MAXIMIZEBOX));
        SetWindowPos(hWnd_, HWND_TOP, 100, 100, 640, 480, SWP_FRAMECHANGED);
        break;
    

    Durch Hinzufügen von WS_VISIBLE klappt plötzlich alles, ein SWP_SHOWWINDOW bzw. ShowWindow() braucht man dann auch nicht mehr 🙂 Offensichtlich wird ein Fenster, dessen Stil WS_VISIBLE nicht enthält, nicht korrekt bei einem Stil- und Größenwechsel gelöscht. Wo liegt denn dann überhaupt der Unterschied darin, WS_VISIBLE beim Erstellen eines Fensters direkt anzugeben oder nach dem Erstellen ShowWindow() aufzurufen? Setzt letztere Funktion nicht genau dieses Flag?



  • Das Flag WS_VISIBLE wird jeweils bei einem Aufruf von ShowWindow(..) gesetzt bzw. gelöscht.
    Das Problem war wahrscheinlich das Flag SWP_SHOWWINDOW. Ich kann es mir nur so erklären, dass durch dieses Flag die alte Position verworfen und nur der neue Bereich des Fensters durch den Desktop-Manager aktualisiert wird. Naja, wie auch immer, es ist ja schön, dass du es geschafft hast.
    PS.: Auch unter Windows 7 macht der Code genau das, was er tun soll.


Anmelden zum Antworten