Entwickler-Ecke

Grafische Benutzeroberflächen (VCL & FireMonkey) - Z-Order von Fenstern


Gausi - Mo 01.03.21 22:09
Titel: Z-Order von Fenstern
Ich bekomme hier eine kleine Krise mit der Z-Reihenfolge von Fenstern einer Anwendung (Windows, VCL).

Grundsätzlich ist das Verhalten in neueren Delphi-Versionen ja so, dass Nebenfenster vor der MainForm angezeigt werden, auch wenn sie nicht den Fokus haben. Das ist manchmal sinnvoll, manchmal aber auch nicht - da möchte man durchaus, dass eine offene Nebenform wieder in den Hintergrund rückt, wenn man in die MainForm klickt.

Dieses alte Verhalten kann man ja erreichen mit

Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TMyOtherForm.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  Params.WndParent := Application.Handle;
end;


Jetzt möchte ich dem User aber zur Laufzeit die Wahl lassen, ob er diese Nebenform vor dem Hauptformular festpinnt, oder eben nicht. Das probiere ich mit

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
if WantStayOnTop then
  FormStyle := fsStayOnTop
  //alternativ: SetWindowPos (Handle, HWND_TOPMOST, -1, -1, -1, -1, SWP_NOMOVE + SWP_NOSIZE)
else
  FormStyle := fsNormal;
  //alternativ: SetWindowPos (Handle, HWND_NOTOPMOST, -1, -1, -1, -1, SWP_NOMOVE + SWP_NOSIZE)


Beides funktioniert nicht, wie gewünscht.

Mit StayOnTop bzw. HWND_TOPMOST
Interessanterweise bewirkt auch fsStayOnTop, dass das Fenster vor allen Anwendungen liegt. Ich dachte eigentlich, dass das mal nur "Top der Application" war ... :gruebel: Zumindest mit der modifizierten CreateParams ist das so. Ohne die Modifizierung hat auch mein StayOnTop-Code keinerlei Auswirkungen mehr.

Ohne StayOnTop

Bekomme ich das ohne irgendwelche Hampeleien (im OnDeactivate etc.) hin, dass eine Form vor der Mainform bleibt (auch wenn die MainForm den Eingabefokus bekommt), aber hinter eventuell zusätzlich geöffneten Dialogen? Oder aber (zur Laufzeit umstellbar) eben nicht?


Th69 - Di 02.03.21 09:31

StayOnTop erscheint mir hier der falsche Weg zu sein, denn dieses gilt (wie du selbst erkannt hast) global für alle Desktop-Windows.

Für Fenster, welche immer oberhalb eines Hauptfensters erscheinen sollen (aber hinter modalen Dialogen), gibt es "Tool Windows": Tool window stay on top [http://www.delphigroups.info/2/b9/412468.html].


Sinspin - Di 02.03.21 10:03

Ich habe bei mir die ganzen alten SetWindowPos und CreateParams rausgeschmissen. Seitdem ist alles viel besser.

Genau, die Tool Windows, die verwendet die IDE ja auch und da klappt es prima.


Gausi - Di 02.03.21 10:08

Danke, schau ich mir mal an. Ein kurzer Schnelltest war negativ, d.h. es funktioniert leider immer noch nicht. Muss ich heute Abend mal in Ruhe an einem Testprojekt austüfteln. Kann sein, dass ich noch an anderer Stelle anderen Code drin habe, der sich damit beißt. Oder es sind die VCL-Styles, oder der Splashscreen beim Start, oder ...

Zumindest weiß ich nun, wo ich ansetzen muss: PopupMode und PopupParent.


Gausi - Di 02.03.21 22:14

Ich glaube, die Dokumentation stimmt da nicht ganz mit dem tatsächlichen Verhalten überein, oder es fehlt noch der Hinweis zu MainFormOnTaskbar. :?

Wenn PopupMode zur Laufzeit geändert werden soll, wird eine Neuerstellung des Fensters notwendig. Und da ich die Hampelei mit PopupMode einfach einfach nicht hin bekomme (und solange MainFormOnTaskbar True ist, hat das anscheinend ohnehin kaum Auswirkungen?), mache ich das jetzt so:


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
procedure TOtherForm.CreateParams(var Params: TCreateParams);
begin
  inherited CreateParams(Params);
  if not MainOptions.OtherFormStayOnTop then // globales Settings-Objekt
    Params.WndParent := Application.Handle;
end;

procedure TOtherForm.cbStayOnTopClick(Sender: TObject); // (CheckBox.OnClick)
begin
  MainOptions.OtherFormStayOnTop := cbStayOnTop.Checked;
  RecreateWnd;
end;

Die ggf. ungünstige Z-Order des OpenDialoges scheint ein Bug mit den VCL-Styles zu sein. Ohne Styles ist das wie gewünscht, mit Styles fällt die OtherForm in den Hintergrund. :?


Th69 - Di 02.03.21 23:30

Ist denn die MyOtherForm auch ein Child der Hauptform?


Gausi - Mi 03.03.21 20:37

Da verstehe ich jetzt nicht ganz, was damit gemeint ist. Aber ob ich die Nebenform automatisch erzeugen lasse, oder per

Delphi-Quelltext
1:
2:
if not assigned(ChildForm) then
  ChildForm := TChildForm.Create(self);

manuell erzeuge, oder per Application.CreateForm(...), ändert nichts an dem Verhalten, dass ich ohne Modifikation der CreateParams nicht weiterkomme ... :gruebel:


Th69 - Do 04.03.21 10:10

OK, ich wollte nur wissen, welchen Owner (und damit auch Parent) du beim Anlegen der Form angegeben hast - hättest ja auch nil setzen können.

Teste das gesamte Verhalten am besten mal in einem neuen Projekt (bevor du es in dein bestehendes Projekt einbaust).