Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Multimedia (https://www.delphipraxis.net/16-multimedia/)
-   -   Delphi Vollständige Sichtbarkeit eines Forms (https://www.delphipraxis.net/111533-vollstaendige-sichtbarkeit-eines-forms.html)

taaktaak 4. Apr 2008 22:46


Vollständige Sichtbarkeit eines Forms
 
Moin, Moin.

Ich arbeite abwechselnd mit einem Notebook und einem PC(mit 2 Monitoren). Da hat es genervt, dass die Wiederherstellung der letzten Fensterposition beim Wechsel von PC auf Notebook immer problematisch war - das hat ja nun mal leider keine 2 Bildschirme eingebaut :(

Nun habe ich mich mal an einer endgültige Problemlösung versucht. Das Ergebnis sorgt nach erstem Test dafür, dass das Programmfenster vollständig sichtbar angezeigt wird, auch wenn aus der INI-Datei Koordinaten zur Positionierung gelesen wurden, mit der das Fenster in der aktuellen Monitorumgebung eigentlich nicht oder nicht vollständig sichtbar dargestellt werden könnte.

1 - Auf welchem Monitor wird Form dargestellt?

Ermittelt, auf welchem Monitor das Form angezeigt wird. Bildet das Form auf Multi-Monitorsystemen Schnittflächen mit mehreren Monitoren, wird derjenige Monitor zurückgeliefert, der die größte Schnittfläche mit dem Form hat. Ist das Form aufgrund einer völlig abweichenden Monitorkonfiguration auf keinem aktuell verfügbaren Monitor sichtbar, wird als Ergebnis der Primärmonitor zurückgeliefert.

Delphi-Quellcode:
function GetCurrentMonitor(Form:TForm):Integer;
var Rect    : TRect;
    i,A,maxA : Integer;            
begin
  Result:=0;
  maxA :=0;
  for i:=0 to Screen.MonitorCount-1 do
    if IntersectRect(Rect,Screen.Monitors[i].BoundsRect,Form.BoundsRect) then begin
      A:=(Rect.Right-Rect.Left)*(Rect.Bottom-Rect.Top);
      if A>maxA then begin maxA :=A;
                           Result:=i;
                           end
      end
end;
2 - Ist Form auf Monitor vollständig sichtbar?

Die Funktion prüft, ob das übergebene Form aktuell vollständig angezeigt wird.

Delphi-Quellcode:
function FormFullInView(Form:TForm):Boolean;
var MonNo,
    MonL,MonW,
    MonT,MonH : Integer;
begin

  MonNo:=GetCurrentMonitor(Form);
 
  MonL :=Screen.Monitors[MonNo].Left;
  MonW :=Screen.Monitors[MonNo].Width;
  MonT :=Screen.Monitors[MonNo].Top;
  MonH :=Screen.Monitors[MonNo].Height;

  Result:=(Form.Left           >=MonL)     and
          (Form.Top            >=MonT)     and
          (Form.Left+Form.Width <=MonL+MonW) and
          (Form.Top +Form.Height<=MonT+MonH)
end;
3 - Vollständige Anzeige des Forms erzwingen

Die Prozedur erzwingt die vollständige Anzeige (zentriert) des Forms auf dem Monitor der mit dem Formular die größte Schnittfläche besitzt. Hat das Form in der aktuellen Konfiguration mit keinem vorhandenen Monitor eine Schnittfläche, wird der primäre Monitor verwendet. Der Aufruf kann (natürlich) erst erfolgen, nachdem Left, Top, Width und Height des Forms festgelegt wurden.

Delphi-Quellcode:
procedure FitFormFullInView(Form:TForm);
var MonNo,
    MonL,MonW,
    MonT,MonH : Integer;
begin
  if not(FormFullInView(Form)) then begin

    MonNo:=GetCurrentMonitor(Form);      
    MonL :=Screen.Monitors[MonNo].Left;
    MonW :=Screen.Monitors[MonNo].Width;
    MonT :=Screen.Monitors[MonNo].Top;
    MonH :=Screen.Monitors[MonNo].Height;

    with Form do begin                  
      Left:=MonL+(MonW-Width ) div 2;    
      Top :=MonT+(MonH-Height) div 2;
      end

   end
end;
Nach Setzen der in der INI-Datei gespeicherten letzten Fensterkoordinaten wird die zuletzt gezeigte Prozedur aufgerufen. Damit wird dann sichergestellt, dass das Form unter jeder Monitorkonfiguration immer vollständig angezeigt wird - hoffe ich jedenfalls :mrgreen:

Warum poste ich das Ganze nun?

Ich würde das gern noch mal von einer weiteren Person testen lassen.
Vielleicht gibt es sogar in der DP-Gemeinde ein Mitglied mit 3, 4 oder noch mehr Monitoren.

// edit : Rechtschreibfehler beseitigt

OldGrumpy 4. Apr 2008 23:14

Re: Vollständige Sichtbarkeit eines Forms
 
Oh das werde ich gleich morgen früh mal ausprobieren, ich hab nämlich gerade das Phänomen dass meine App mit XP-Manifest zwar im Modus "maximiert" auf dem Bildschirm erscheint, aber trotzdem deutlich kleiner als der Bildschirm ist. Erst zweimaliges Klicken aufs Border Icon maximiert die Applikation dann korrekt (grummel!) - ohne XP-Manifest klappts. Mal sehen ob Dein Ansatz da Abhilfe schafft :)

[edit=Phoenix]Text wiedergeholt.. Mfg, Phoenix[/edit]

taaktaak 6. Apr 2008 16:53

Re: Vollständige Sichtbarkeit eines Forms
 
Moin, Moin.
Gibt's hier im Forum kein Mitglied, das mehr als zwei Monitore hat und das mal testen kann?

sx2008 6. Apr 2008 19:34

Re: Vollständige Sichtbarkeit eines Forms
 
Also ich hab leider keinen 2. Monitor, aber ich hab deine Funktionen im Kopf überprüft und würde sagen: Daumen hoch, sieht sehr gut aus.
GetCurrentMonitor ist piffig gelöst.

Vielleicht statt TForm die Klasse TCustomForm verwenden.
FitFormFullInView könnte man vielleicht besser lösen.
Sobald ein Form die Monitorfläche um ein Pixel verlässt, wird es in x- und y-Richtung zentriert.
Es würde aber reichen, das Form nur soweit zu verschieben, bis es nirgends mehr übersteht.
Also 1.) GetCurrentMonitor aufrufen 2.) ermitteln, um wieviele Pixel das Form in x- und/oder y-Richtung übersteht 3.) Form um genau diese x/y-Beträge so verschieben, dass es vollständig im Monitor ist.
Ich würde dazu nicht FormFullInView aufrufen, sondern gleich in die Rechnung einsteigen.

taaktaak 6. Apr 2008 21:01

Re: Vollständige Sichtbarkeit eines Forms
 
Vielen Dank für den Check :hi:
- TCustomForm werde ich ändern,
- den zweiten Punkt erst einmal mal in der Praxis austesten. Hatte das Zentrieren gewählt, damit besonders deutlich wird, dass das Form vom Programm abweichend von der letzten Position neu positioniert wurde

Assertor 21. Jun 2008 20:09

Re: Vollständige Sichtbarkeit eines Forms
 
Hi taaktaak,

Zitat:

Zitat von taaktaak
Ich würde das gern noch mal von einer weiteren Person testen lassen.

Etwas spät, aber hiermit getan ;)

Ich habe es eben mit 2 Monitoren in unterschiedlicher Anordnung getestet (1,2 / 2,1).

Ich sehe auf Anhieb zwei Probleme:

1) Du berücksichtigest bei einem Resize nicht die Höhe der Taskbar und auf welchem der Monitore diese ist. WorkareaRect wäre hier der richtige Ansatz.

2) Was ist, wenn ein Benutzer wirklich das Form über mehrere Monitore vergrößern will? Es gibt ja auch Multimonitorsysteme ohne Displayrahmen, die zusammen einen großen Desktop ergeben. Dabei würde Deine Lösung immer auf einen einzelnen Monitor verkleinern.

Zitat:

Zitat von taaktaak
- den zweiten Punkt erst einmal mal in der Praxis austesten. Hatte das Zentrieren gewählt, damit besonders deutlich wird, dass das Form vom Programm abweichend von der letzten Position neu positioniert wurde

Hast Du daran schon etwas verändert? Dieser Ansatz gefällt mir auch besser.

Ich habe gerade genau das gleiche Problem und sitze in Griffweite eines Multimonitorsystems... Ist das für Dich noch aktuell?

Gruß Assertor

Assertor 21. Jun 2008 23:42

Re: Vollständige Sichtbarkeit eines Forms
 
So,

hier mal ein Lösungsansatz für die angesprochenen Probleme:

Damit erledigt FitFormFullInView zusammen mit seinen Hilfsfunktionen die folgenden Aufgaben (für Einzel- oder Multi-Monitor):
  • Wenn das Fenster in den gesamten Desktop passt, wird es unverändert gelassen (für gewünschte, große Multimonitor-Fenster)
  • Ist das Fenster zum Teil außerhalb des gültigen Gesamtdesktops, wird es auf dem Monitor mit dem größten Anteil zentriert angezeigt
  • Ist das Fenster vollständig außerhalb des gültigen Gesamtdesktops, wird es auf dem Primary-Monitor zentriert angezeigt
  • Ist das Fenster immer noch zu groß für den jeweiligen Monitor, wird es entsprechend in Höhe und Breite angepasst
  • Bei der Anpassung wird die Taskbar berücksichtigt (egal ob Links, Oben, Rechts, Unten)
Delphi-Quellcode:
function GetCurrentMonitor(AForm: TCustomForm): Integer;
var
  Rect: TRect;
  i, A, maxA: Integer;
begin
  {
    man kann sich diese Funktion auch sparen und einfach AForm.Monitor aufrufen,
    wobei diese u.U. nicht so gründlich und schön auf die Maximalnutzung prüft
  }
  Result := 0;
  maxA := 0;
  for i := 0 to Screen.MonitorCount-1 do
    if IntersectRect(Rect, Screen.Monitors[i].BoundsRect, AForm.BoundsRect) then
    begin
      A := (Rect.Right-Rect.Left) * (Rect.Bottom-Rect.Top);
      if A > maxA then
      begin
        maxA := A;
        Result:=i;
      end;
    end;
end;

function FormFullInView(AForm: TCustomForm): Boolean;
var
  MonNo,
  MonL, MonW,
  MonT, MonH: Integer;
begin
  MonNo := GetCurrentMonitor(AForm);

  MonL := Screen.Monitors[MonNo].WorkareaRect.Left;
  MonW := Screen.Monitors[MonNo].WorkareaRect.Right - Screen.Monitors[MonNo].WorkareaRect.Left;
  MonT := Screen.Monitors[MonNo].WorkareaRect.Top;
  MonH := Screen.Monitors[MonNo].WorkareaRect.Bottom - Screen.Monitors[MonNo].WorkareaRect.Top;

  // handle multi-monitor windows
  if (AForm.Left >= Screen.DesktopLeft) and
     (AForm.Top >= Screen.DesktopTop) and
     ((AForm.Left + AForm.Width) <= (Screen.DesktopWidth + Screen.DesktopLeft)) and
     ((AForm.Top + AForm.Height) <= (Screen.DesktopHeight + Screen.DesktopTop))
  then
    Result := True
  else
    Result:= (AForm.Left >= MonL) and (AForm.Top >= MonT) and
      (AForm.Left + AForm.Width <= MonL + MonW) and
      (AForm.Top + AForm.Height <= MonT + MonH);
end;

procedure FitFormFullInView(AForm: TCustomForm);
var
  MonNo,
  MonL, MonW,
  MonT, MonH: Integer;
begin
  if not (FormFullInView(AForm)) then
  begin
    MonNo := GetCurrentMonitor(AForm);
    MonL := Screen.Monitors[MonNo].WorkareaRect.Left;
    MonW := Screen.Monitors[MonNo].WorkareaRect.Right - Screen.Monitors[MonNo].WorkareaRect.Left;
    MonT := Screen.Monitors[MonNo].WorkareaRect.Top;
    MonH := Screen.Monitors[MonNo].WorkareaRect.Bottom - Screen.Monitors[MonNo].WorkareaRect.Top;
    with AForm do
    begin
      if Height > MonH then
        Height := MonH;
      if Width > MonW then
        Width := MonW;    
      Left := MonL + (MonW-Width) div 2;
      Top := MonT + (MonH-Height) div 2;
    end;
  end;
end;
Im Ergebnis finde ich die Zentrierung auch besser.

Nützlich ist die Funktion, wenn z.B. 1) ein Anwender seinen Monitor / seine Grafikkarte durch ein Modell mit anderer Standard-Auflösung ersetzt und ein Programm dann mit diesen neuen Einstellungen klar kommen muß oder 2) eine Anwendung mit Einstellungen für einen Multimonitor-PC z.B. auf einem USB-Stick mobil am Notebook eingesetzt wird.

Gruß Assertor

sirarcher 9. Sep 2008 09:28

Re: Vollständige Sichtbarkeit eines Forms
 
Hi,

beim From die "Position" auf "poScreenCenter" stellen. Dann wirds auch sauber ausgerichtet.

Gruss

Assertor 9. Sep 2008 09:34

Re: Vollständige Sichtbarkeit eines Forms
 
Hi,

Zitat:

Zitat von sirarcher
beim From die "Position" auf "poScreenCenter" stellen. Dann wirds auch sauber ausgerichtet.

Ein fast 3 Monate alter Thread, den Du da auskramst... Aber Dein Vorschlag hilft nicht, da hier Zentrierung nur bei geänderten Monitorverhältnissen gewünscht ist. Die oben gepostete Lösung funktioniert doch einwandfrei...

Warum also der Post :?:

Gruß Assertor

taaktaak 9. Sep 2008 10:14

Re: Vollständige Sichtbarkeit eines Forms
 
Moin, Moin.
... weil ich sonst gar nicht mitbekommen hätte, dass Assertor meinen Code verbessert hast. Prima!

Werde allerdings die Funktionalität
Zitat:

Wenn das Fenster in den gesamten Desktop passt, wird es unverändert gelassen (für gewünschte, große Multimonitor-Fenster)
optional gestalten. Noch mal Danke für die Optimierung :hi:

Assertor 9. Sep 2008 19:37

Re: Vollständige Sichtbarkeit eines Forms
 
Hi taaktaak,

Zitat:

Zitat von taaktaak
Moin, Moin.
... weil ich sonst gar nicht mitbekommen hätte, dass Assertor meinen Code verbessert hast. Prima!

Bitte gerne! Du hast mir mit Deiner Vorlage ja auch geholfen - und das nicht das erste mal soweit ich mich erinnere.

Gruß Assertor

:cheers:

taaktaak 7. Sep 2009 10:39

Re: Vollständige Sichtbarkeit eines Forms
 
Von RWarnecke wurde in meinem aktuellen Projekt "rzDelphiGuide" ein Fehler entdeckt, der von der Prozedur FitFormFullInView() produziert wird. Für alle Interessierten hier die kleine aber wichtige Korrektur:

Position/Größe dürfen nicht korrigiert werden, wenn das Formular maximiert dargestellt wird.
Der Anfang der Prozedur ist daher wie folgt zu ändern:

Delphi-Quellcode:
procedure FitFormFullInView(AForm: TCustomForm);
var
  MonNo,
  MonL, MonW,
  MonT, MonH: Integer;
begin
  if (AForm.WindowState<>wsMaximized) and
     not (FormFullInView(AForm))     then
  begin
    ..
    ..

Assertor 8. Sep 2009 00:01

Re: Vollständige Sichtbarkeit eines Forms
 
Hi taaktakk,

Zitat:

Zitat von taaktaak
Für alle Interessierten hier die kleine aber wichtige Korrektur:

Position/Größe dürfen nicht korrigiert werden, wenn das Formular maximiert dargestellt wird.

Jein, ich nutze unsere ;) Prozeduren etwas anders, d.h. ich lade immer die zuvor gespeicherte Position und Maximiere oder Normalisiere dann das Fenster.

Vorteil: Wenn der Benutzer die Anwendung maximiert startet, kann er trotzdem zur vorherigen Größe zurückkehren. Und diese vorherige Größe ist dann per FitFormFullInView an die aktuellen Gegebenheiten der Bildschirme angepasst.

Zusätzlich hatte ich noch ein paar Funktionen für das Speichern der tatsächlichen "normalen Größe" trotz maximiertem Fenster geschrieben und mit einer AutoSize garniert, so daß ein Erststart der Anwendungen zu einem Fenster von 80% Größe (bzw. bliebig) auf dem Primärmonitor führt.

Bei Interesse kann ich die Codeschnipsel hier posten.

Gruß Assertor

himitsu 8. Sep 2009 10:13

Re: Vollständige Sichtbarkeit eines Forms
 
vorallem das rausbekommen wäre für mich interessant,

obwohl mir da grad ein Gedankenblitz gekommen ist
- bei Beenden Form ausblenden (Hide), wiederherstellen (demaximieren :lol: ) und dann die Werte auslesen.

Assertor 8. Sep 2009 10:22

Re: Vollständige Sichtbarkeit eines Forms
 
Hi Himitsu,

Zitat:

Zitat von himitsu
vorallem das rausbekommen wäre für mich interessant,

obwohl mir da grad ein Gedankenblitz gekommen ist
- bei Beenden Form ausblenden (Hide), wiederherstellen (demaximieren :lol: ) und dann die Werte auslesen.

Lol, ja das wäre auch ein Lösung. Nicht geeignet für Epileptiker :mrgreen:

Hier aus meinem Nähkästchen:
Delphi-Quellcode:
procedure SaveWindowState(AConfigFile: TXMLConfig;
  AForm: TForm; const Section: UnicodeString;
  const AllowMinimize: Boolean = False;
  const AdjustNonClientHeight: Integer = 0);
var
  p: TWindowPlacement;
  r: TRect;
begin
  Assert(AConfigFile <> nil);
  Assert(AForm <> nil);
  with AConfigFile do begin
    if (AForm.WindowState <> wsMinimized) or (AllowMinimize) then
    begin
      {
        calculate window's normal size and position using Windows API call -
        the form's Width, Height, Top and Left properties will give maximized
        window size if form is maximized, which is not what we want here.

        AdjustNonClientHeight is used to support libraries like DevExpress
        Ribbon components which adjust the form size after loading to support
        drawing in glass/nonclient area under Vista.
      }
      p.length := SizeOf(TWindowPlacement);
      if not (GetWindowPlacement(AForm.Handle, @p)) then
      begin
        AppLog.Log(evtError, {$IFDEF UNICODE}Format{$ELSE}UnicodeFormat{$ENDIF}(
          SWindowPositionGetError, [SysErrorMessage(GetLastError)]));
      end
      else
      begin
        r := p.rcNormalPosition;
        WriteInteger(Section, 'Left', r.Left);
        WriteInteger(Section, 'Top', r.Top);
        WriteInteger(Section, 'Width', r.Right - r.Left);
        if AdjustNonClientHeight = 0 then
          WriteInteger(Section, 'Height', r.Bottom - r.Top)
        else
          WriteInteger(Section, 'Height', r.Bottom - r.Top +
            AdjustNonClientHeight);
      end;
    end;
    case AForm.WindowState of
      wsMinimized: if AllowMinimize then
                      WriteString(Section, 'WindowState', 'Minimized');
      wsMaximized: WriteString(Section, 'WindowState', 'Maximized');
    else
      WriteString(Section, 'WindowState', 'Normal'); // wsNormal
    end;
  end;
end;
Ist so aus einer meiner Units und wird so nicht kompilieren, aber die Idee sollte rüberkommen: TWindowPlacement.rcNormalPosition ist die einfachste Lösung dafür.

Beim Laden dann umgekehrt:
Delphi-Quellcode:
[Pseudocode]
    // try to load saved window positions (even if maximized to preserve size)
    if ValueExists(Section, 'Left') or ValueExists(Section, 'Top') or
       ValueExists(Section, 'Width') or ValueExists(Section, 'Height') then
    begin
      // all values or at least one of them
      x1 := ReadInteger(Section, 'Left', AForm.Left);
      y1 := ReadInteger(Section, 'Top', AForm.Top);
      x2 := ReadInteger(Section, 'Width', AForm.Width);
      y2 := ReadInteger(Section, 'Height', AForm.Height);
      // do the positions differ from current position?
      if (x1 <> AForm.Left) or (x2 <> AForm.Width) or
         (y1 <> AForm.Top) or (y2 <> AForm.Height) then
      begin
        AForm.SetBounds(x1, y1, x2, y2);
      end;
    end
    else
    begin
      // center form (with default size e.g. 80%) if values are not found
      CenterFormToMonitor(AForm);
    end;

    // always check if the current form fits into monitor/desktop area
    FitFormFullInView(AForm);

    // load window state
    // Setzte AForm.WindowState auf wsMaximized, wsNormal
    // (oder wsMinimized wenn wir das erlauben wollen)
Gruß Assertor


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:57 Uhr.

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