Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Delphi Fensterposition zurücksetzen (https://www.delphipraxis.net/203461-fensterposition-zuruecksetzen.html)

haentschman 19. Feb 2020 09:07

Fensterposition zurücksetzen
 
Hallöle...:P

Nur aus Interesse. Unsere Anwendung speichert die Positionen der einzelnen Fenster. Soweit so gut. :wink:

Geht der Mitarbeiter an einen anderen Platz, wo die Monitore vertauscht sind, ist ggf. das Fenster nicht sichtbar. (Position außerhalb des Desktops)
Ich habe mir selbst eine simple Logik dafür implementiert. Die Frage ist, gibt es was vom System, um das Fenster in den sichtbaren Bereich zu bringen? :wink:

Diskussion eröffnet... :wink:

KodeZwerg 19. Feb 2020 09:13

AW: Fensterposition zurücksetzen
 
MakeFullyVisible
MakeFullyVisible

So etwas eventuell? Keine Ahnung ob es das jetzt noch gibt?

Uwe Raabe 19. Feb 2020 09:17

AW: Fensterposition zurücksetzen
 
Gibt es: Vcl.Forms.TCustomForm.MakeFullyVisible

haentschman 19. Feb 2020 10:08

AW: Fensterposition zurücksetzen
 
Danke... :P

Man lernt nicht aus. Ich habe meine Logic ausgetauscht. :thumb:

Delphi-Quellcode:
procedure TfoBase.RestoreForm(Name: string);
var
  Desktop: TRect;
begin
  FIniPath := FFolders.Items[ftPositionFolder] + Name; // wiederverwendbar für Save
  prsBase.StorageName := FIniPath;
  prsBase.RestoreFrom;

  // wieder einblenden
  if Self.WindowState = wsMinimized then
  begin
    Self.WindowState := wsNormal;
  end;

  // Prüfung ob im Desktop
  Desktop := Screen.DesktopRect;
  if Self.Left < Desktop.Left then
  begin
    Self.MakeFullyVisible(Screen.Monitors[0]);
  end;
  if Self.Left > Desktop.Width then
  begin
    Self.MakeFullyVisible(Screen.Monitors[Screen.MonitorCount - 1]);
  end;

  SaveForm;
end;

uligerhardt 19. Feb 2020 10:23

AW: Fensterposition zurücksetzen
 
Du könntest auch MSDN-Library durchsuchenSetWindowPlacement & Co. verwenden. Das sorgt automatisch dafür, dass das Fenster nicht komplett verschwindet.

TigerLilly 21. Feb 2020 09:01

AW: Fensterposition zurücksetzen
 
Gibt es MakeFullyVisible nur für die VCL? Nicht FMX?

Uwe Raabe 21. Feb 2020 09:56

AW: Fensterposition zurücksetzen
 
Das ist leider so.

TigerLilly 21. Feb 2020 10:19

AW: Fensterposition zurücksetzen
 
Ja, ich hab grad im Source nachgesehen. Für FMX ist das wohl aufwändiger, weil die plattformspezifischen Dienste ins Spiel kommen.

himitsu 21. Feb 2020 10:54

AW: Fensterposition zurücksetzen
 
Hatte das vorher nicht gekannt und mir sowas selbst gebaut.

Ist jetzt kein Hexenwerk ... einfach gucken ob Form in einem Monitor liegt und wenn nicht, dann die Werte anpassen.
Und dabei lassen sich dann auch sondefälle behandeln.

Ist jetzt auch VCL, aber lässt sich bestimmt leicht anpassen, wenn es dort sowas wie Screens gibt. (Vcl.Forms.Screens, Vcl.Forms.Application, usw. auf Fmx.Forms stellen)

Wobei, für Android/iOS kann man es ignoeieren, wenn/da dort alle Fenster Vollbild sind und bei Anschluß eines externen Monitors der Screen nur gespiegelt wird.
Und wenn nicht, dann kann man Android/iOS zumindestens fragen wie groß der "eine" Screen ist und das als WorkArea nutzen, oder einfach die Position seiner Hauptform verwenden, die Vollbild ist und daher weiß so sie liegt.
Brauchst also nur noch was für MAC und Linux kannst'e mit FMX eh vergessen.

WorkArea = das ohne Taskleiste und andere AppBars

Delphi-Quellcode:
procedure CorrectWindowPosition(Form: TForm);
begin
  if Form.WindowState <> wsNormal then
    Exit;

  Monitor := Form.Monitor;
  if not Assigned(Monitor) then
    Monitor := Application.MainForm.Monitor;
 
  if Form.Width > RectWidth(Monitor.WorkareaRect) then
    Form.Width := RectWidth(Monitor.WorkareaRect);
  if Form.Height > RectHeight(Monitor.WorkareaRect) then
    Form.Height := RectHeight(Monitor.WorkareaRect);

  if Form.Left < Monitor.WorkareaRect.Left then
    Form.Left := Monitor.WorkareaRect.Left;
  if Form.Top < Monitor.WorkareaRect.Top then
    Form.Top := Monitor.WorkareaRect.Top;

  if Form.Left + Form.Width >= Monitor.WorkareaRect.Right then
    Form.Left := Monitor.WorkareaRect.Right - Form.Width;
  if Top + Form.Height >= Monitor.WorkareaRect.Bottom then
    Form.Top := Monitor.WorkareaRect.Bottom - Form.Height;
end;
Für RectWidth/RectHeight gibt es inzwischen einen ClassHelper mit Property Width/Height.

Dann noch paar Optimierungen, wie z.B. die Größe/Position nicht schrittweise anzupassen, sondern das nur einmal zuzuweisen.
Delphi-Quellcode:
procedure CorrectWindowPosition(Form: TForm; Monitor: TMonitor = nil);
var
  R: TRect;
  Left, Top, Width, Height: Integer;
begin
  if Form.WindowState <> wsNormal then
    Exit;

  Left  := Form.Left;
  Top   := Form.Top;
  Width := Form.Width;
  Height := Form.Height;

  if not Assigned(Monitor) then begin
    Monitor := Screen.MonitorFromRect(Bounds(Left, Top, Width, Height)); //Monitor := Form.Monitor; aber falls Form noch nicht sichtbar war...
    if not Assigned(Monitor) then
      Monitor := Application.MainForm.Monitor;
  end;
  R := Monitor.WorkareaRect;

  if Width > RectWidth(R) then
    Width := RectWidth(R);
  if Height > RectHeight(R) then
    Height := RectHeight(R);

  if Left < R.Left then
    Left := R.Left;
  if Top < R.Top then
    Top := R.Top;

  if Left + Width >= R.Right then
    Left := R.Right - Width;
  if Top + Height >= R.Bottom then
    Top := R.Bottom - Height;

  if not EqualRect(Form.BoundsRect, Bounds(Left, Top, Width, Height)) then
    Form.BoundsRect := Bounds(Left, Top, Width, Height);
end;
Nun noch paar "persönliche" Sonderfälle rein, die MakeFullyVisible natürlich nicht wissen kann.
Und hier kann es auch eine Form auf dem "ganzen" Desktop sein, bzw. die über mehrere Monitore geht.
Delphi-Quellcode:
const
  ScreenOverDesktop = TMonitor(1); // Multimonitor: über gesamten Desktop
  ScreenOverMonitor = TMonitor(0); // über einen einzelnen Monitor

procedure CorrectWindowPosition(Form: TForm; Monitor: TMonitor = ScreenOverMonitor);
var
  R:       TRect;
  W20, H33, FormGlassFrameWidth: Integer;
  Left, Top, Width, Height: Integer;
begin
  if Form.WindowState <> wsNormal then
    Exit;

  Left  := Form.Left;
  Top   := Form.Top;
  Width := Form.Width;
  Height := Form.Height;
  W20 := Form.Width div 5; // darf bis zu 20% links oder rechts überstehen
  H33 := Form.Height div 3; // darf bis zu 33% unten überstehen

  {$REGION 'SETFORMPOS: Toolform ausrichten wenn Modulform verschoben wurde'}
  if Form.ClassNameIs('TToolForm') and (Form.Owner is TForm) then begin
    W20 := 0;
    H33 := 0;
    FormGlassFrameWidth := 4;
    if not NoNewCommonControls then // WinXP schmalere Ränder
      FormGlassFrameWidth := 8;
    if ((Form.Owner as TForm).BorderStyle in [bsToolWindow, bsSizeToolWin]) or ((TForm(Form.Owner).FormStyle = fsNormal) and (TForm(Form.Owner).BorderStyle in [bsSizeable, bsDialog])) then
      FormGlassFrameWidth := FormGlassFrameWidth - IfThen(NoNewCommonControls, 3, 5);

    if Form.Parent = Application.MainForm then begin //FormStyle=fsMDIChild
      Left := Max(Max(TFormAccsess(Form.Owner).GetClientOrigin.X - 34, Application.MainForm.Left - 30) - Application.MainForm.Left - FormGlassFrameWidth, 0);
      Top := Max(    TFormAccsess(Form.Owner).GetClientOrigin.Y,     Application.MainForm.Top)      - Application.MainForm.Top;
    end else begin
      Left := TForm(Form.Owner).Left - 24 - FormGlassFrameWidth;
      Top := TForm(Form.Owner).Top + 20;
    end;
  end;
  {$ENDREGION}

  if Form.FormStyle = fsMDIChild then begin
    R := Application.MainForm.ClientRect;
    Dec(R.Bottom, RibbonHeight); // MainForm.RibbonMain.Height (Top/Left ist verschoben)
    Dec(R.Right, 25);            // MainForm.PanelLeft.Width
    { TODO : alternativ alle Komponenten, mit Align <> alNone und alClient, von R abrechnen}
  end else if Assigned(Form.Parent) then
    R := Form.Parent.ClientRect
  else if Monitor = ScreenOverDesktop then begin
    // Bugfix: Fenster über mehrere Monitore http://redmine.prodat-sql.de/issues/5855
    { TODO : Schwarze Bereiche, bei unterschiedlich großen oder verschobenen Monitoren, werden nicht beachtet }
    R := Screen.DesktopRect;
  end else begin
    //R := Screen.MonitorFromRect(Form.Monitor.BoundsRect, mdNearest).WorkareaRect;
    if not Assigned(Monitor) then begin
      Monitor := Screen.MonitorFromRect(Bounds(Left, Top, Width, Height)); //Monitor := Form.Monitor;
      if not Assigned(Monitor) then
        Monitor := Application.MainForm.Monitor;
    end;
    R := Monitor.WorkareaRect;
  end;

  {$REGION 'SETFORMPOS: Fensterposition prüfen und anpassen'}
  if Width > RectWidth(R) then
    Width := RectWidth(R);
  if Height > RectHeight(R) then
    Height := RectHeight(R);

  if Left + W20 < R.Left then
    Left := R.Left;
  if Top {+ H33} < R.Top then
    Top := R.Top;

  if Left + Width - W20 > R.Right then
    Left := R.Right - Width;
  if Top + Height - H33 > R.Bottom then
    Top := R.Bottom - Height;
  {$ENDREGION}

  if not EqualRect(Form.BoundsRect, Bounds(Left, Top, Width, Height)) then begin
    Form.BoundsRect := Bounds(Left, Top, Width, Height); // Form.Left/Top/Width/Height := ...
    ProcessDrawMessages(False);
  end;
end;

DieDolly 22. Feb 2020 10:02

AW: Fensterposition zurücksetzen
 
Zitat:

Gibt es MakeFullyVisible nur für die VCL? Nicht FMX?
Wenn das Fenster nicht sichtbar ist, setzt man es einfach auf den ersten Monitor. Dafür braucht man keine großartigen Funktionen mit tollen Namen, die es nur für VCL aber nicht FMX gibt.

Delphi-Quellcode:
if (Screen.MonitorFromWindow(AForm.Handle, mdNull) = nil) and (Screen.MonitorCount > 0) then
 begin
  // AForm.Top := (Screen.Monitors[0].Height - AForm.Height) div 2;
  // AForm.Left := (Screen.Monitors[0].Width - AForm.Width) div 2;
 end;
Und falls es MonitorFromWindow für FMX nicht gibt, dann haben die FMX-Leute eben Pech gehabt ;)

himitsu 22. Feb 2020 16:51

AW: Fensterposition zurücksetzen
 
In der VCL gibt es auch direkt TForm.Monitor für den Monitor wo die Form zum größten Teil drauf ist, (also der Monitor auf dem der Mittelpunkt der Form liegt)
aber im FMX nicht und in FMX.Forms gibt es nicht so eine Screen-Klasse, wie in VCL.Forms.

Gut, man kann natürlich problemlos via den APIs in Winapi.Windows selber das mit den Monitoren machen, aber natürlich nur im Windows.

haentschman 27. Feb 2020 10:05

AW: Fensterposition zurücksetzen
 
Liste der Anhänge anzeigen (Anzahl: 2)
Moin...:P

Meine neue Logic funktioniert nicht. :evil:

Die einzelnen Monitore haben nicht die erwarteten Größen. (siehe Bilder)
Kann jemand logisch erklären?

Danke...:wink:

TigerLilly 27. Feb 2020 10:12

AW: Fensterposition zurücksetzen
 
Die Koordinaten geben an, welchen Bereich des Desktops ein Monitor abdeckt.

himitsu 27. Feb 2020 10:13

AW: Fensterposition zurücksetzen
 
Hab jetzt nur in der VCL geguckt, da gibt es Delphi-Referenz durchsuchenTMonitor.MonitorNum? und .Primary für den Ersten.
Und die Reihenfolge in der Liste ist einfach entsprechend dem, wie MSDN-Library durchsuchenEnumDisplayMonitors das liefert.

[edit]
Nee, MonitorNum ist List.Count beim Einfügen, also das Gleiche. :oops:
Tja, dann ist Windows böse zu dir.

PS: https://docs.microsoft.com/de-de/win...-display-setup

haentschman 27. Feb 2020 10:24

AW: Fensterposition zurücksetzen
 
Zitat:

Und die Reihenfolge in der Liste ist einfach entsprechend dem, wie MSDN-Library durchsuchenEnumDisplayMonitors das liefert.
...das erklärt nicht die verschobene Reihenfolge und Größen.

Das sind die Werte die im Tool zu sehen sind.
Delphi-Quellcode:
procedure TfoMonitor.FormShow(Sender: TObject);
begin
  grpMonitor.Caption := 'Monitornummer ' + IntToStr(Monitor.MonitorNum + 1);
  lbl5.Caption := IntToStr(Monitor.Left);
  lbl6.Caption := IntToStr(Monitor.Top);
  lbl7.Caption := IntToStr(Monitor.Height);
  lbl8.Caption := IntToStr(Monitor.Width);
end;

himitsu 27. Feb 2020 10:29

AW: Fensterposition zurücksetzen
 
PS: Hatte meine Antwort oben nochmal revidiert.
Delphi übernimmt einfach die Reihenfolge von der WinAPI und GetMonitorInfo(Monitor.Handle, ...) liefert auch keine zusätzlichen Infos. (nur Primary und die Rects)
Wenn Windows das schon falsch rausgibt, dann kannst da über die TScreen-Klasse nichts weiter machen.

Falls du die Reihenfolge zur Laufzeit geändert hast, dann "sollte" Delphi eigenlich drauf reagieren und die Daten neu einlesen.

haentschman 27. Feb 2020 10:38

AW: Fensterposition zurücksetzen
 
Daß heist, daß man sich auf die Monitoranordnung, was aus dem System kommt, nicht verlassen kann. :evil: Darauf baut aber die Logik. :roll:

himitsu 27. Feb 2020 10:54

AW: Fensterposition zurücksetzen
 
Zumindestens nicht komplett.
Primary = 1, not Primary = >1 und die Positionen sollten ja stimmen.

bist nicht der Einzige:
https://www.vb-paradise.de/index.php...dentifizieren/
https://living-sun.com/c/132927-a-re...tors-edid.html
...

> Wie Sie vielleicht wissen, listet Windows dieAnzeigegeräte in der Reihenfolge, in der Sie sie verbinden (oder in der Tat überhaupt nicht auf einer Regel basieren?).

https://docs.microsoft.com/de-de/win...nelement-value
> Beachten Sie, dass sich einige Teile dieser Muster abhängig von der Installations Reihenfolge ändern können.

vielleicht noch
https://www.delphipraxis.net/184581-...-auslesen.html

haentschman 27. Feb 2020 11:03

AW: Fensterposition zurücksetzen
 
Zitat:

bis nicht der Einzige:
...da bin ich aber froh. :stupid:

Ich werde meine Logik anpassen. Ich werde mir die Monitore separat auslesen und die linke Seite passend setzen. :?

himitsu 27. Feb 2020 11:22

AW: Fensterposition zurücksetzen
 
Ich hab die Lösung:

Du löst das Monitor identifizieren vom OS aus und via OCR liest du dann die Numern vom Screenshot ab. :lol:

KodeZwerg 27. Feb 2020 12:08

AW: Fensterposition zurücksetzen
 
Zitat:

Zitat von haentschman (Beitrag 1458514)
Ich werde mir die Monitore separat auslesen und die linke Seite passend setzen.

Da habe ich immer noch das problem gehabt das sich Delphi selbst nicht aktualisiert, wenn man 'nen Monitor ausstöpselt wird der immer noch gelistet, Screen Resolution zwar 0x0 aber index noch da... des war allerdings noch mit Delphi 2009/2010 kompilate, vielleicht ist's ja mit Rio besser, leider noch nicht getestet.

CodeX 9. Jun 2020 15:34

AW: Fensterposition zurücksetzen
 
Da ich mich selber gerade ziemlich mit Unregelmäßigkeiten bei der Fensterpositionierung herumgeärgert habe, möchte ich an dieser Stelle noch anmerken, dass MakeFullyVisible nicht die Eigenheiten von Windows 10 berücksichtigt.
Windows 10 hat nämlich noch einen unsichtbaren Rand von normalerweise 7px. Siehe im Detail hier:
https://stackoverflow.com/questions/...isible-borders

Hat der Anwender also beispielsweise das Fenster links angedockt und man speichert diese Position beim Beenden der Anwendung mit (x=0,y=0), möchte man nach dem Programmstart die Anwendung ja auch an die gleiche Position wieder setzen, was eigentlich ja auch geht, aber führt man dann MakeFullyVisible aus, weil man sichergehen möchte, dass die Anwendung nicht irgendwo versteckt ist, weil sich die angeschlossenen Monitore geändert haben, dann wird das Fenster auf x=7,y=0 verschoben.

Die einzige Option, die ich das sehe, wäre den Code von MakeFullyVisible zu kopieren und mit entsprechenden Toleranzen an den Rändern anzupassen.

WladiD 9. Jun 2020 15:58

AW: Fensterposition zurücksetzen
 
Fensterpositionen sind in der Tat seit MS in Windows 10 die Monitor-API v2 umgesetzt hat eine komplexe Angelegenheit geworden. Das glaubt man erst, wenn man sich damit beschäftigt hat.

Für etwas Input will ich auf meine Unit WD.WindowTools aus WinDomina verweisen.

Die dort enthaltenen Funktionen GetWindowRectDominaStyle und SetWindowPosDominaStyle funktionieren zumindest in meinen Testumgebungen zuverlässig.

Nur falls sich jemand mit der Materie beschäftigen möchte...


Alle Zeitangaben in WEZ +1. Es ist jetzt 06:38 Uhr.

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