GELÖST: Mehrere Formulare im Vordergrund anzeigen (Anwendung selbst nicht)



  • Hallo liebe Community

    Ausgangslage:

    Ich habe eine Anwenung, bei dieser können zur Laufzeit weitere Formulare generiert werden.
    Dabei sollen diese vom Typ (WS_EX_TOPMOST) sein. D.h. sie sollen stets im Vordergrund ersichtlich sein.
    Die Hauptanwendung, soll diese Eigenschaft jedoch nicht teilen.

    Probleme:

    • Ist die Applikation geöffnet, so werden diese Fenster angezeigt jedoch nicht im Vordergrund.
    • Wird die Anwendung minimiert, so werden auch die erstellten Fenster minimiert.
    • Wird die Anwendung in den Tray verschoben (Bei OnClose; Tray-Symbol aktivieren; Visible=false; Action=caNone). So verlieren die Dialoge ebenfalls ihre Position als TopWindow. Zudem kann die Eigenschaft auch nicht mehr aktiviert werden. Weder vom Programm selbst noch von aussen (Die Meldefenster verlieren den Wert sofort wieder, oder nehmen ihn erst garnicht an).

    Lösungsansätze:

    Die Probeleme könnte ich teilweise entschärfen, indem ich die Fenster beim Minimierungs- und schliessvorgang neu zeichne und wieder in den Vordergrund setze.
    Dies klappt soweit auch. Jedoch bin ich der Auffassung, dass dies auch anders gehen sollte.
    Zudem stellten sich bei diesem Lösungsansatz weitere Problematiken heraus. So konnte Das Hauptfenster nur noch spärlich minimiert und maximiert werden (Es musste mehrmals auf das Taskleistensymbol geklickt werden oder es wurde erst garnicht mehr geöffnet)

    Frage:

    Meine Frage lautet nun. Wie kann folgendes realisiert werden.
    Denn es kann ja wohl nicht so schwer sein, ein paar Fenster zu erstelln, welche ein bisschen Unabhängigkeit vom Hauptformular verlangen.
    Ich mache mir schon Überlegungen, ob ich dieses Meldeform in eine eigene EXE-Datei packe und diese dann ausführe.
    Jedoch macht mir dann die Liste der laufenden Prozess sorgen, da es schon mal auftreten kann, das dieser 5 oder mehrmal ausgeführt wird.
    -> Sieht halt einfach nicht schön aus. (Auch wenns den Anwender wahrscheinlich nicht interessiert)

    Code:

    Die Erstellung dieser Fenster wird wie folgt vorgenommen.
    Dabei hat das Formular in den Eigenschaften den Wert WindowStyle=fsStayOnTop.

    TF_MeldeForm *newMeldeForm = new TF_MeldeForm(Application);
    
     ... 
    
     newMeldeForm->Show();
    

    Vielen Dank für eure Aufmerksamkeit und produktiven Lösungsvorschläge.



  • Hallo Zusammen.

    Allgemein:
    Nach etlichen Stunden der Arbeit und des testens, konnte ich nun eine Lösung ausfindig machen.
    Dabei werden sämtliche Fehler in Bezug zu erstellten Unterformularen mit der Eigenschaft TOPMOST (WS_EX_TOPMOST) abgefangen und behoben.

    Beschreibung - Lösung:
    Um die auftretenden Eigenschaftsverluste zu eliminieren, gilt es im zu erstellenden TOPMOST-Window folgende Messages abzufangen und zu bearbeiten.
    - WM_WINDOWPOSCHANGING
    - WM_SHOWWINDOW
    - WM_ACTIVATE

    Eine ausführliche Beschreibung der abgefangenen Ereignisse und aus welchem Grund dies so gemacht wurde, ist unten ersichtlich.
    Wichtig ist aber noch, dass der Formularstyle mit wsStayOnTop definiert wurde.

    Kurzbeschreibung Anwendung
    Um folgende Zeile zu verstehen, ist allenfalls ein kleiner Informationsteil zum Applikationsdesign notwendig.

    static_cast<UnicodeString>(cWindowName).Pos(L"Pendenzenplaner:")<=0
    

    Das Hauptformular erstellt wärend der Laufzeit (oder im Konstruktor) etliche Unterformulare (Meldefenster), welche im Titel stets mit "Pendenzenplaner:" beginnen.
    Genau diese Fenster, sollten stets im Vordergrund sein.

    Code - Lösung:

    void __fastcall TF_MeldeForm::vfTWM_HandleMessage(TMessage& Msg)
    {
    //Hier werden die einkommenden Messages abgefangen, um sicherzustellen, dass sich das Fenster stets im Vordergrund befindet.
    
    	wchar_t cWindowName[20]={'\0'};
    	HWND hwActiveWindow;
    
    	switch (Msg.Msg)
    	{
    		//Bei WINPOSCHANGING wir das Ausblenden und der Verlust der Z-Order abgefangen.
    		case WM_WINDOWPOSCHANGING:
    			//Ermitteln des aktiven Fensters
    			hwActiveWindow=GetActiveWindow();
    			GetWindowText(hwActiveWindow,cWindowName,20);
    
    			//Erweitern des Flags um SWP_NOZORDER. Dies verhindert, dass der Parameter hwndInsertAfter beachtet wird
    			//und so die Eigenschaft TOPMOST verliert.
    			//Die Ausdrücke haben folgende Bedeutung
    			// 1. Anweisung muss ausgeführt werden, wenn das letzt aktive Fenster das Main-Fenster war.
    			//    Da sonst die Eigenschaft beim Minimieren des Fensters verloren gehr.
    			// 2. Wenn der hwndInsertAfter nicht mehr HWND_TOP entspricht, so ist das Ändern der Eigenschaft zwingend notwendig
    			// 3. Die Eigenschaft darf nur angepasst werden, wenn vorhin ein Fenster der Applikation aktiv war.
    			//    Sonst wird das Fenster beim anklicken nicht vor ein anderes Fenster der Applikation gestellt.
            		//    -> Erst nach 2 maligem anklicken
    			if (hwActiveWindow==hwMainForm || (reinterpret_cast<WINDOWPOS*>(Msg.LParam)->hwndInsertAfter!=HWND_TOP && static_cast<UnicodeString>(cWindowName).Pos(L"Pendenzenplaner:")<=0))
    				reinterpret_cast<WINDOWPOS*>(Msg.LParam)->flags |= SWP_NOZORDER;
    
    			//Den Parameter des Ausplendens löschen (SWP_HIDEWINDOW)
    			//Ist nur notwendig, wenn das Fenster im Konstruktor des Hauptformulares erstellt wird.
    			//Denn dann erhält das Fenster neben der SHOWWINDOW-Message auch noch den oben genannten Parameter
    			reinterpret_cast<WINDOWPOS*>(Msg.LParam)->flags &= ~SWP_HIDEWINDOW;
    			break;
    
    		//Neben dem Ausplenden via WM_WINDOWPOSCHANGING muss auch SHOWWINDW abgefangen werden.
    		//Dabei wird der WParam auf True gesetzt, das Heisst, das Fenster wird sichtbar.
    		case WM_SHOWWINDOW:
    			Msg.WParam=true;
    			break;
    
    		//War zuletzt ein Fenster der Anwendung aktiv (nicht das Hauptfenster).
    		//So scheint der Taskbutton durch das minimieren der Anwendung ein falsches Handle zu erhalten.
    		// -> Beim klicken auf den Taskbutton wird das Fenster nicht mehr aktiv.
    		//Aus diesem Grund ist es notwendig, das Fenster selbst in den Vordergrund zu holen.
    		// -> Dies jedoch nur, wenn das Fenster nicht durch einen Mausklick aktiviert wird.
    		// -> und das Hauptfenster sichtbar ist und nicht im Tray,....
    		case WM_ACTIVATE:
    			if (Msg.WParam==WA_ACTIVE && IsWindowVisible(hwMainForm))
    				SetForegroundWindow(hwMainForm);
    			break;
    	}
    
        //Weiterverarbeiten der Message
    	OldWndProc(Msg);
    }
    

    Vielleicht hat jemand ja das selbe Problem und hoffe, dass Ihm mit dieser Lösungsvariante geholfen werden konnte.
    Sollten noch Fragen oder Anmerkungen auftreten, so werde ich mich bemühen, diese bestmöglich zu klären.


Anmelden zum Antworten