Delphi-PRAXiS
Seite 2 von 2     12   

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi AlwaysOnTop mit mehreren Formularen (https://www.delphipraxis.net/204233-alwaysontop-mit-mehreren-formularen.html)

Dalai 9. Mai 2020 21:47

AW: AlwaysOnTop mit mehreren Formularen
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1464128)
Das wird nur gehen, wenn die anderen Anwendungen nicht dasselbe vorhaben und ihre eigenen Fenster auch als TOPMOST deklarieren.

Wie ich bereits schrieb, ist mir das "Highlander-Prinzip" in diesem Fall bekannt. Ist das Problem denn mit meinem Testprogramm nachvollziehbar, wenn die beiden Formulare nebeneinander positioniert sind? Denn wie gesagt interessiert mich nicht, ob Formular 2 der Anwendung über Formular 1 derselben Anwendung liegt, denn das passiert nicht und das teste ich auch nicht. Wichtig für mich ist ausschließlich, dass beide Formulare meiner Anwendung (die immer nebeneinander liegen) über den Fenstern anderer Anwendungen (ohne TOPMOST) liegen. Es geht da auch um nichts Großes, das eine Formular ist 65x31 Pixel, das andere ~170x20 Pixel.

Zwischenzeitlich hab ich das zweite Form in eine eigene Anwendung überführt (weil ich für ein anderes Projekt fix eine Lösung brauchte), aber ich bin dennoch an einer Lösung innerhalb einer Anwendung interessiert.

Grüße
Dalai

himitsu 9. Mai 2020 22:18

AW: AlwaysOnTop mit mehreren Formularen
 
siehe #5 ?

[edit]
Hab's aber grad nochmal probiert ... ich glaub Delphi bzw. die VCL ist Schuld.

Vorhin falsch geguckt, denn so ist es aktuell in Windows 10 + Delphi 10.3:
* das zweite Fenster ist immer über dem Ersten
* wären Beide gleich (StayOnTop oder nicht), müsste jeweils das Aktive oben sein
* vermutlich irgendwas in Richtung PopupMode, aber das steht (standardmäßig) eigentlich auf pmNone :grueble:

[edit2]
Delphi-Quellcode:
procedure TCustomForm.CreateParams(var Params: TCreateParams);
...
        case LPopupMode of
          pmNone:
            begin
              if Application.MainFormOnTaskBar then
              begin
                // FCreatingMainForm is True when the MainForm is
                // being created, Self = Application.MainForm during CM_RECREATEWND.
                if FCreatingMainForm or (Self = Application.MainForm) then
                  WndParent := 0
                else
                  if Assigned(Application.MainForm) and Application.MainForm.HandleAllocated then
                  begin
                    WndParent := Application.MainFormHandle;
                    if WndParent = Application.MainForm.Handle then
                    begin
                      if Application.MainForm.PopupChildren.IndexOf(Self) < 0 then
                        Application.MainForm.PopupChildren.Add(Self); <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
                      FreeNotification(Application.MainForm);
                    end;
                  end
                  else
                    WndParent := Application.Handle;
              end
              else
              begin
                WndParent := Application.Handle;
                SetWindowLong(WndParent, GWL_EXSTYLE, GetWindowLong(WndParent, GWL_EXSTYLE) and not WS_EX_TOOLWINDOW);
              end;
            end;
          pmAuto:
            begin
              if FCreatingMainForm then
                WndParent := 0 // A main form can't be parented to another form
              else
Keine Ahnung wer auf diese bescheuerte Idee gekommen ist auch bei NONE etwas zu machen.

Lösung:
Und statt OnActivate, ist sowieso CreateWnd besser. (auch wenn Beides geht)
Delphi-Quellcode:
type
  TForm3 = class(TForm)
    procedure FormCreate(Sender: TObject);
  protected
    procedure CreateWnd; override;
  end;

var
  Form3: TForm3;

implementation

{$R *.dfm}

procedure TForm3.CreateWnd;
begin
  inherited;
  SetWindowPos(Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE);
end;

procedure TForm3.FormCreate(Sender: TObject);
begin
  PopupMode := TPopupMode(9);
end;
[Edit3]
Oder Application.OnGetMainFormHandle benutzen und dort
Delphi-Quellcode:
HWND(-1)
zurückgeben.

Uwe Raabe 9. Mai 2020 22:41

AW: AlwaysOnTop mit mehreren Formularen
 
Zitat:

Zitat von himitsu (Beitrag 1464144)
Es kommt drauf an, wie man es sieht/auslegt

TopMost = über ALLEM (da ginge nur Einer, so ala Highlander)
TopMost = über allem, was nicht TopMost ist

Wohl eher nicht wie man das sieht/auslegt, sondern wie MS das implementiert hat.

Zitat:

HWND_TOPMOST: Places the window above all non-topmost windows. The window maintains its topmost position even when it is deactivated.
Um ein TOPMOST Fenster über alle anderen (auch TOPMOST) zu legen, kann man ein SetWindowPos mit HWND_TOP senden. Das platziert ein Fenster so weit vorn wie möglich - bei einem TOPMOST Fenster halt ganz nach vorn. Das hält aber auch nur solange bis ein anderes TOPMOST Fenster (z.B. einer anderen Anwendung) sich davor legt.

Man kann auch noch mit PopupParent experimentieren. Ist das nicht gesetzt, wird implizit das MainForm verwendet. Das hat aber nur Auswirkungen auf die eigenen Anwendung.

himitsu 9. Mai 2020 23:09

AW: AlwaysOnTop mit mehreren Formularen
 
Ja, "normal" war es so, dass bei mehreren TopMost beide vor allen anderen Nicht-TopMost blieb
und bei mehreren TopMost dort jeweils das ganz obe, was man zuletzt angeklickt/fokusiert hat.

Wenn man den "neuen" Mist im TCustomForm.CreateParams "repariert", dann ist es auch wieder so.
Wenn die Entwickler das Verhalten ändern wollen, dann dürfen sie gern pmAuto als Default festlegen, aber niemals pmNone kaputt machen.

Dalai 14. Mai 2020 07:30

AW: AlwaysOnTop mit mehreren Formularen
 
OK, nachdem ich wieder etwas Luft habe, hab mir das jetzt im Detail angeschaut. Leider musste ich an mehreren Stellen drehen, um einige Nebeneffekte zu unterbinden.


Zitat:

Zitat von himitsu (Beitrag 1464149)
Lösung:
Und statt OnActivate, ist sowieso CreateWnd besser. (auch wenn Beides geht)

Wenn man die Fenster immer OnTop setzen will, dann mag das besser sein. Wenn man dem Nutzer die Wahl lassen will, ist das ungeeignet, denn CreateWnd wird vor dem FormCreate gefeuert, so dass man gar keine Möglichtkeit hat, etwas zu setzen. CreateWnd wird zwar offenbar vor dem OnShow nochmals gerufen, aber ich mag Eindeutigkeit (und werde daher beim OnActivate bleiben).

Zitat:

Delphi-Quellcode:
procedure TForm3.FormCreate(Sender: TObject);
begin
  PopupMode := TPopupMode(9);
end;

Wenn ich das richtig verstehe, zwingt das die Routine im TCustomForm.CreateParams dazu, keinen speziellen Code zur Bearbeitung des Window Parent auszuführen. Was bedeutet das genau?

Dummerweise hat das zur Folge, dass die Form2 im Beispiel einen eigenen Button in der Taskleiste bekommt. Das kann ich gar nicht brauchen.

Nach einigen Versuchen hab ich eine Lösung gefunden, aber ich frag lieber nach, ob das eine saubere Variante ist.

Unit1:
Delphi-Quellcode:
procedure TForm1.FormCreate(Sender: TObject);
begin
    if bShowForm2 then begin
        Fform2:= TForm2.Create(nil);
        Fform2.BorderStyle:= bsNone;
        Fform2.PopupParent:= Self;
    end;
end;
Restliche Funktionen der Unit wie gehabt.

Unit2:
Delphi-Quellcode:
procedure TForm2.FormCreate(Sender: TObject);
begin
    Self.PopupMode:= TPopupMode(99);
end;
---

Eine andere Variante kam mir gerade aufgrund eines anderen Projektes in den Sinn.
Unit1: wie im OP.
[EDIT]Der Aufruf von TForm2.Create muss natürlich etwas anders aussehen, aber der Rest bleibt gleich.
Delphi-Quellcode:
procedure TForm1.FormCreate(Sender: TObject);
begin
    if bShowForm2 then begin
        Fform2:= TForm2.Create(nil, Self.Handle);
        Fform2.BorderStyle:= bsNone;
    end;
end;
[/EDIT]

Unit2:
Delphi-Quellcode:
type
  TForm2 = class(TForm)
    procedure FormActivate(Sender: TObject);
  private
    FWndParent: HWND;
  protected
    procedure CreateParams(var Params: TCreateParams); override;
  public
    constructor Create(AOwner: TComponent; AWndParent: HWND); reintroduce; overload;
  end;
 
implementation
 
constructor TForm2.Create(AOwner: TComponent; AWndParent: HWND);
begin
    Self.FWndParent:= AWndParent;
    inherited Create(AOwner);
end;

procedure TForm2.CreateParams(var Params: TCreateParams);
begin
    inherited;
    Params.WndParent:= Self.FWndParent;
end;

procedure TForm2.FormActivate(Sender: TObject);
begin
    uGUIHelper.AlwaysOnTop(True, Self.Handle);
end;
Vorteil: Kein Button erscheint für Form2 in der Taskleiste und ein Setzen des PopupMode entfällt, was den Nebeneffekt hat, dass es sogar im Delphi 5 funktioniert ;).

Meinungen zu den beiden Lösungsvarianten? Ist eine besser geeignet als die andere? Und wenn ja, warum?

Grüße
Dalai


Alle Zeitangaben in WEZ +1. Es ist jetzt 03:09 Uhr.
Seite 2 von 2     12   

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz