Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Delphi Formular Position speichern, Generell wenn geschlossen wird oder bei ModalResult mrOK (https://www.delphipraxis.net/191532-formular-position-speichern-generell-wenn-geschlossen-wird-oder-bei-modalresult-mrok.html)

Kidi 25. Jan 2017 10:06

Formular Position speichern, Generell wenn geschlossen wird oder bei ModalResult mrOK
 
Hallo alle zusammen,
wie die Überschrift sagt, beschäftigt es mich wann die Formular Position gespeichert werden soll.
Wie handhabt ihr das, ich bin da ziemlich unsicher was der bessere Fall ist.

Mfg
Didi

a.def 25. Jan 2017 10:10

AW: Formular Position speichern, Generell wenn geschlossen wird oder bei ModalResult
 
Ich handhabe das so, dass die Größe und Position bei Resize oder Move meines Formulares in ein Array geschrieben wird.
Diese vier Werte speichere ich dann im OnClose.

Dazu kann man z.B. verwenden
Delphi-Quellcode:
procedure WMFormMoved(var msg: TWMMove); message WM_MOVE;
// und
procedure FormResize(Sender: TObject);

Aviator 25. Jan 2017 11:07

AW: Formular Position speichern, Generell wenn geschlossen wird oder bei ModalResult
 
Zitat:

Zitat von a.def (Beitrag 1359891)
Ich handhabe das so, dass die Größe und Position bei Resize oder Move meines Formulares in ein Array geschrieben wird.
Diese vier Werte speichere ich dann im OnClose.

Warum in einem Array zwischenspeichern wenn du die Werte direkt beim Beenden abrufen kannst? So wie du es jetzt machst läufst du nur unnötig Gefahr, dass du, aus welchem Grund auch immer, mal ein solches Event verpasst und deine Werte nicht mehr stimmen.

a.def 25. Jan 2017 11:12

AW: Formular Position speichern, Generell wenn geschlossen wird oder bei ModalResult
 
Zitat:

Zitat von Aviator (Beitrag 1359898)
Warum in einem Array zwischenspeichern wenn du die Werte direkt beim Beenden abrufen kannst? So wie du es jetzt machst läufst du nur unnötig Gefahr, dass du, aus welchem Grund auch immer, mal ein solches Event verpasst und deine Werte nicht mehr stimmen.

Das mit dem "erst beim Beenden speichern" hatte ich vorher so gemacht. Aber wenn Größe und Position eines Formulars welches nicht das Hauptformular ist gespeichert werden soll gab das Probleme (falsche Werte usw).
Daher speichere ich zwischen. Funktioniert wunderbar und die Zahlen sind immer aktuell.


Edith sagt
Man muss die alten Werte ja eh aus der Konfiguration auslesen und irgendwo zuweisen. Also ist das Zwischenspeichern kein Beinbruch :P

Slipstream 25. Jan 2017 12:13

AW: Formular Position speichern, Generell wenn geschlossen wird oder bei ModalResult
 
Zitat:

Zitat von a.def (Beitrag 1359903)
Das mit dem "erst beim Beenden speichern" hatte ich vorher so gemacht. Aber wenn Größe und Position eines Formulars welches nicht das Hauptformular ist gespeichert werden soll gab das Probleme (falsche Werte usw). Daher speichere ich zwischen. Funktioniert wunderbar und die Zahlen sind immer aktuell.

Was spricht dagegen, erstens zwei private Methode zum Lesen und Speichern von Formulargrösse und -position einzubauen, in der direkt diese fünf Parameter in Registry, Datenbank oder Inidatei gespeichert werden?

Und was spricht zweitens dagegen, diese Methode dann in der OnClose-Ereignisbehandlung aufzurufen?

Ich mach das schon immer so und hatte damit noch nie Probleme. Hier ein Beispiel, wie man aus einer Datenbank (die vielleicht auch von mehreren Clients verwendet wird), die individuellen Daten zur Wiederherstellung der individuellen Programmoptionen ausliest und wieder zurückschreibt, der Einfachheit halber reduziert auf Fensterposition, -grösse und Windowstate:

Delphi-Quellcode:
Procedure FormSoundSo.FormParamsRead;
begin
  Left := DM.QueryUser.FieldByName('SOUNDSO_LEFT').AsInteger;
  Top := DM.QueryUser.FieldByName('SOUNDSO_TOP').AsInteger;
  Width := DM.QueryUser.FieldByName('SOUNDSO_WIDTH').AsInteger;
  Height := DM.QueryUser.FieldByName('SOUNDSO_HEIGHT').AsInteger;
  if DM.QueryUser.FieldByName('SOUNDSO_MAX').AsBoolean then
     Windowstate := wsMaximized else
     Windowstate := wsNormal;
end;

Procedure FormSoundSo.FormParamsWrite;
var
  Maxi : Boolean;
begin
  Maxi := Windowstate = wsMaximized;
  Windowstate := wsNormal;
  DM.QueryUser.Edit;
  DM.QueryUser.FieldByName('SOUNDSO_LEFT').AsInteger := Left;
  DM.QueryUser.FieldByName('SOUNDSO_TOP').AsInteger Top;
  DM.QueryUser.FieldByName('SOUNDSO_WIDTH').AsInteger := Width;
  DM.QueryUser.FieldByName('SOUNDSO_HEIGHT').AsInteger := Height;
  DM.QueryUser.FieldByName('SOUNDSO_MAX').AsBoolean := Maxi;
  DM.QueryUser.Post;
end;

Procedure FormSoundSo.FormShow(Sender: TObject);
begin
  FormParamsRead;
end;

Procedure FormSoundSo.FormClose(Sender : TObject; Var Action : TCloseAction);
begin
  FormParamsWrite;
end;
Genauso (war mir jetzt zu aufwendig, das hier reinzutippen) würde man das beim Lesen von bzw. Schreiben in eine Inidatei oder Registry machen. Wenn du das jedesmal zwischenspeicherst, hast du das doppelt gespeichert. Ich weiß keinen Grund, diese Daten im Programm doppelt zu speichern.

dataspider 25. Jan 2017 12:33

AW: Formular Position speichern, Generell wenn geschlossen wird oder bei ModalResult
 
Liste der Anhänge anzeigen (Anzahl: 1)
Hi

Ich finde es nützlich, die NormalPosition zu sichern und den State und zu prüfen, ob das Formular außerhalb des Screen ist.

ich habe dazu einen Form - Helper (Anlage).

Aufruf dann (z.B. in Datei speichern, bei mir landet das in der DB):

Delphi-Quellcode:

procedure TfrmMain.AfterConstruction;
Var
  AStream: TMemoryStream;
begin
  inherited;
  AStream := TMemoryStream.Create;
  try
    if FileExists(SFsBin) then
    begin
      AStream.LoadFromFile(SFsBin);
      LoadFormState(AStream);
    end;
  finally
    AStream.Free;
  end;
end;

procedure TfrmMain.BeforeDestruction;
Var
  AStream: TMemoryStream;
begin
  AStream := TMemoryStream.Create;
  try
    SaveFormState(AStream);
    AStream.SaveToFile(SFsBin);
  finally
    AStream.Free;
  end;
  inherited;
end;
Nicht vergessen: frm.Helper in Uses

Ist jetzt zwar mit Stream, aber das Prinzip ist sicher erkennbar.

Frank

a.def 25. Jan 2017 12:41

AW: Formular Position speichern, Generell wenn geschlossen wird oder bei ModalResult
 
Guck mal hier. So kann man auch auf außerhalb-des-sichtbaren-Bereichs prüfen.

Delphi-Quellcode:
procedure checkFormPosition(aFormToSetPos: TForm);
begin
 if Screen.MonitorFromWindow(aFormToSetPos.Handle, mdNull) = nil then
  begin
   // Form is outside of any monitor. Move to center of main monitor
   aFormToSetPos.Top := (Screen.Monitors[0].Height - aFormToSetPos.Height) div 2;
   aFormToSetPos.Left := (Screen.Monitors[0].Width - aFormToSetPos.Width) div 2;
  end;
end;

rokli 25. Jan 2017 13:51

AW: Formular Position speichern, Generell wenn geschlossen wird oder bei ModalResult
 
Hi Kidi,

ich verwende 2 Proceduren, mit denen ich die Werte in INI-Dateien schreibe (neben all den anderen Werten, die man so braucht):

Delphi-Quellcode:
procedure TfrmMain.p_IniRead;
{-------------------------------------------------------------------------------
   INI Datei einlesen
-------------------------------------------------------------------------------}
var
   Ini      : TIniFile;
begin
   Ini      := TIniFile.Create(ChangeFileExt( Application.ExeName, '.INI' ) );
   try
      Top       := Ini.ReadInteger( 'Windows Form Settings', 'Top', 100 );
      Left       := Ini.ReadInteger( 'Windows Form Settings', 'Left', 100 );
      Height    := Ini.ReadInteger( 'Windows Form Settings', 'Height', 100);
      Width    := Ini.ReadInteger( 'Windows Form Settings', 'Width', 100);
      if Ini.ReadBool( 'Windows Form Settings', 'InitMax', false ) then
         WindowState          := wsMaximized
      else
         WindowState          := wsNormal;
   finally
      Ini.Destroy;
   end;
end;

procedure TfrmMain.p_IniWrite;
{-------------------------------------------------------------------------------
   INI Datei wegschreiben
-------------------------------------------------------------------------------}
var
   Ini      : TIniFile;
begin
   Ini       := TIniFile.Create(ChangeFileExt( Application.ExeName, '.INI' ) );
   try
      Ini.WriteDateTime('Userdata', 'LastLogOut', now());
      Ini.WriteInteger( 'Windows Form Settings', 'Top', Top);
      Ini.WriteInteger( 'Windows Form Settings', 'Left', Left);
      Ini.WriteInteger( 'Windows Form Settings', 'Height', Height);
      Ini.WriteInteger( 'Windows Form Settings', 'Width', Width);
      Ini.WriteBool(    'Windows Form Settings', 'InitMax', WindowState = wsMaximized );
   finally
      Ini.Destroy;
   end;
end;
Den p_IniRead lese ich im OnCreate der Form und den p_IniWrite führe ich im OnClose aus. Und wenn Du die Positionen von weiteren Fenstern benötigst, dann erweiterst Du dir das entsprchend.

rokli 25. Jan 2017 13:54

AW: Formular Position speichern, Generell wenn geschlossen wird oder bei ModalResult
 
Ein großer Vorteil einer Ini-Datei ist, dass man ganz einfach z. B. den Form1.LEFT Wert ändern kann, und damit bei einer 2-Schirm Lösung das Programm von einem Bildschirm auf den anderen schieben kann, auch dann wenn man die Form nach dem Programmstart nicht sieht, weil sie außerhalb des Bildschirmes gelandet ist.

uligerhardt 25. Jan 2017 13:56

AW: Formular Position speichern, Generell wenn geschlossen wird oder bei ModalResult
 
Ich benutze für sowas MSDN-Library durchsuchenGetWindowPlacement/MSDN-Library durchsuchenSetWindowPlacement. Damit erspart man sich z.B. die Prüfung auf zwischenzeitlich nicht mehr vorhandene Monitore etc. Aufrufen tue ich das normalerweise im OnShow/OnHide.
Hier mal die Schreib-Routine als Beispiel:
Delphi-Quellcode:
procedure WriteWindowPlacement(AIni: TCustomIniFile; const Section, NamePrefix: string; Form: TCustomForm);
var
  wp: TWindowPlacement;
  sl: TStringList;
  s: string;
begin
  wp.length := SizeOf(TWindowPlacement);
  GetWindowPlacement(Form.Handle, @wp);

  sl := TStringList.Create;
  sl.Capacity := 9;
  try
    sl.Append(IntToStr(wp.showCmd));
    sl.Append(IntToStr(wp.ptMinPosition.X));
    sl.Append(IntToStr(wp.ptMinPosition.Y));
    sl.Append(IntToStr(wp.ptMaxPosition.X));
    sl.Append(IntToStr(wp.ptMaxPosition.Y));
    sl.Append(IntToStr(wp.rcNormalPosition.Left));
    sl.Append(IntToStr(wp.rcNormalPosition.Top));
    sl.Append(IntToStr(wp.rcNormalPosition.Right));
    sl.Append(IntToStr(wp.rcNormalPosition.Bottom));
    s := EncodeDelimitedText('(', ';', ')', sl); // Hier "sl" irgendwie in einen einzeiligen String wandeln (sl.DelimitedText oder so).
  finally
    sl.Free;
  end;

  AIni.WriteString(Section, NamePrefix + 'WindowPlacement', s);
end;

HolgerX 25. Jan 2017 14:04

AW: Formular Position speichern, Generell wenn geschlossen wird oder bei ModalResult
 
Hmm..

@rokli

Und wenn Du mehr wie ein Formular speichern wills:

Delphi-Quellcode:
procedure FromProp_IniRead(AForm : TCustomForm);
// INI Datei einlesen
var
  Ini : TIniFile;
  tmpFormName : string;
begin
  Ini     := TIniFile.Create(ChangeFileExt( Application.ExeName, '.INI' ) );
  try
    tmpFormName := AForm.Name;
    AForm.Top   := Ini.ReadInteger(tmpFormName, 'Top',   AForm.Top );
    AForm.Left  := Ini.ReadInteger(tmpFormName, 'Left',  AForm.Left );
    AForm.Height := Ini.ReadInteger(tmpFormName, 'Height', AForm.Height);
    AForm.Width := Ini.ReadInteger(tmpFormName, 'Width', AForm.Width);
    if Ini.ReadBool( tmpFormName, 'InitMax', false ) then
      AForm.WindowState         := wsMaximized
    else
      AForm.WindowState         := wsNormal;
  finally
    Ini.Destroy;
  end;
end;

procedure FromProp_IniWrite(AForm : TCustomForm);
// INI Datei wegschreiben
var
  Ini : TIniFile;
  tmpFormName : string;
begin
  Ini      := TIniFile.Create(ChangeFileExt( Application.ExeName, '.INI' ) );
  try
    tmpFormName := AForm.Name;
    Ini.WriteInteger(tmpFormName, 'Top',   AForm.Top);
    Ini.WriteInteger(tmpFormName, 'Left',  AForm.Left);
    Ini.WriteInteger(tmpFormName, 'Height', AForm.Height);
    Ini.WriteInteger(tmpFormName, 'Width', AForm.Width);
    Ini.WriteBool(tmpFormName, 'InitMax', AForm.WindowState = wsMaximized );
  finally
    Ini.Destroy;
  end;
end;

rokli 25. Jan 2017 14:17

AW: Formular Position speichern, Generell wenn geschlossen wird oder bei ModalResult
 
cool, Holger!
:cyclops:

t.roller 25. Jan 2017 14:45

AW: Formular Position speichern, Generell wenn geschlossen wird oder bei ModalResult
 
How to remember a window's size, state and position

http://www.delphidabbler.com/zip/demos/wdwstatedemo.zip

mm1256 25. Jan 2017 15:00

AW: Formular Position speichern, Generell wenn geschlossen wird oder bei ModalResult
 
Zitat:

Zitat von HolgerX (Beitrag 1359931)
Hmm..

Delphi-Quellcode:
procedure FromProp_IniRead(AForm : TCustomForm);
  ...
  Ini     := TIniFile.Create(ChangeFileExt( Application.ExeName, '.INI' ) );
  ...
end;

So was sollte man eigentlich verbieten, auch wenn es nur ein Beispiel ist. Es gibt welche, die übernehmen den Unsinn dann sogar noch mit copy&paste :stupid:

Slipstream 25. Jan 2017 22:30

AW: Formular Position speichern, Generell wenn geschlossen wird oder bei ModalResult
 
Zitat:

Zitat von rokli (Beitrag 1359928)
Ein großer Vorteil einer Ini-Datei ist, dass man ganz einfach z. B. den Form1.LEFT Wert ändern kann, und damit bei einer 2-Schirm Lösung das Programm von einem Bildschirm auf den anderen schieben kann, auch dann wenn man die Form nach dem Programmstart nicht sieht, weil sie außerhalb des Bildschirmes gelandet ist.

Und wenn du keine Ini hast und das Formular oder die Anwendung trotzdem auf einem gestern noch vorhandenen, heute aber fehlendem, weil defekten Bildschirm virtuell "erscheint", aber nicht zu sehen ist, dann kannst du dieses Formular mit altbewährter Windowstechnik ebenfalls auf den sichtbaren Bereich herüberschieben:

Alt-Space lässt das Menü aufklappen. Ist das Formular maximiert, muss man es erst normalisieren. Dann fährt man runter zum Eintrag Verschieben und schiebts einfach rüber. Das ist jedem Windows-Anwender zuzutrauen, das Herumfummeln in Ini-Dateien dagegen weniger.

a.def 25. Jan 2017 22:34

AW: Formular Position speichern, Generell wenn geschlossen wird oder bei ModalResult
 
Ich zitiere mich mal einfach selber. Man braucht kein Alt+Space und all den Quark :stupid:
Die Alt+Space-Geschichte ist rein theoretisch jedem zuzutrauen. Wer es aber kennt und anwenden kann, ist die andere Frage. Ich gehe von einem Prozentsatz von <25 aus.

Zitat:

Zitat von a.def (Beitrag 1359914)
Guck mal hier. So kann man auch auf außerhalb-des-sichtbaren-Bereichs prüfen.

Delphi-Quellcode:
procedure checkFormPosition(aFormToSetPos: TForm);
begin
 if Screen.MonitorFromWindow(aFormToSetPos.Handle, mdNull) = nil then
  begin
   // Form is outside of any monitor. Move to center of main monitor
   aFormToSetPos.Top := (Screen.Monitors[0].Height - aFormToSetPos.Height) div 2;
   aFormToSetPos.Left := (Screen.Monitors[0].Width - aFormToSetPos.Width) div 2;
  end;
end;


Slipstream 26. Jan 2017 01:15

AW: Formular Position speichern, Generell wenn geschlossen wird oder bei ModalResult
 
Nein, da hast du was falsch verstanden. Deiner Methode in Delphi, das Erscheinen eines Formulars ausserhalb eines gültigen Bildschirmbereichs zu vermeiden, wollte ich auf keinen Fall widersprechen, das ist sinnvoll und richtig. Mir ging es nur darum zu zeigen, dass es für einen normalen Windows-Benutzer einfacher ist, mit Alt-Space (und das ist kein Quatsch, das ist ein sinnvolles Feature von Windows) eine Anwendung in den sichtbaren Bereich zu verschieben, anstatt in irgend einer Ini-Datei herumzufurwerkeln und die Struktur der Ini am Ende noch zu zerstören.

rokli 26. Jan 2017 05:58

AW: Formular Position speichern, Generell wenn geschlossen wird oder bei ModalResult
 
Moin!

Das "herumfummeln" in der INI gehört genauso zu den Dingen, die aus der EDV Abteilung gemacht werden, wie die Windows Tastenkombinationen zu kennen, die Du, Slipstream, da genannt hast. Den Prozentsatz der Anwender, die das wissen, würde ich eher wesentlich kleiner als die genannten 25 % einschätzen.

Da ist die Lösung CheckFormPosition von a.def ja viel, viel besser.

@Otto: Warum sollte man das verbieten?

Gruß

DeddyH 26. Jan 2017 06:47

AW: Formular Position speichern, Generell wenn geschlossen wird oder bei ModalResult
 
Zitat:

Zitat von rokli (Beitrag 1359984)
@Otto: Warum sollte man das verbieten?

Der vorgesehene Pfad für (installierte) Programme liegt ja im Programme-Ordner. Da hat der Normalbenutzer aber standardmäßig keine Schreibrechte, von daher wird das so nicht funktionieren. Die bessere Option wäre APPDATA bzw. COMMON_APPDATA, diese Verzeichnisse lassen sich ja ermitteln.

freimatz 26. Jan 2017 07:21

AW: Formular Position speichern, Generell wenn geschlossen wird oder bei ModalResult
 
Zitat:

Zitat von uligerhardt (Beitrag 1359929)
Ich benutze für sowas MSDN-Library durchsuchenGetWindowPlacement/MSDN-Library durchsuchenSetWindowPlacement. Damit erspart man sich z.B. die Prüfung auf zwischenzeitlich nicht mehr vorhandene Monitore etc. Aufrufen tue ich das normalerweise im OnShow/OnHide.
...

Danke. Das war mir neu - und auch m.E. das einzige Richtige, also selber etwas zu "basteln".

t.roller 26. Jan 2017 07:38

AW: Formular Position speichern, Generell wenn geschlossen wird oder bei ModalResult
 
GetWindowPlacement ist auch in der Antwort #13 enthalten...

rokli 26. Jan 2017 09:22

AW: Formular Position speichern, Generell wenn geschlossen wird oder bei ModalResult
 
@DeddyH: Da hast Du natürlich recht!

mm1256 26. Jan 2017 13:08

AW: Formular Position speichern, Generell wenn geschlossen wird oder bei ModalResult
 
Zitat:

Zitat von rokli (Beitrag 1359984)
@Otto: Warum sollte man das verbieten?

Hallo Rolf,

Detlef hat das ja schon beantwortet. Doch das alleine ist es nicht, was mich an dem Beispiel etwas gestört hat.

1) Code-Optimierung: Sobald man mehrfach auf irgend etwas zugreift, in diesem Fall den Namen der INI-Datei, gehört sich dieser Teil ausgelagert. Hintergrund: Wenn man etwas ändert will/muss, dann macht man das an einer einzigen Stelle, und neben nicht an mehreren Stellen. Womöglich vergisst man dann was, und schon wundert man sich im Idealfall selber, im schlimmsten Fall der Kunde, dass irgendwas nicht funktioniert. Darum als Vorschlag mal die erste Optimierung:

Delphi-Quellcode:
function GetIniFileName: string;
begin
  Result := IncludeTrailingBackslash(UserDatenDirectory)+'MeinIniName.ini';
end;

procedure IniLesen;
begin
  With TIniFile.Create(GetIniFileName) do
  try
    // Werte lesen
  finally
    Free;
  end;
end;

procedure IniSchreiben;
begin
  With TIniFile.Create(GetIniFileName) do
  try
    // Werte schreiben
  finally
    Free;
  end;
end;
2) Code-Design: Die Werte für Top, Left, Width und Height eines Formulares setzt man nicht einzeln. Dazu verwendet man
Code:
SetBounds()
Warum ist das besser? Wenn man im Formular z.B. einen OnResize-Event hat, der schlimmstenfalls eine Positions-Neuberechnung vieler Komponenten durchführt, dann wird dieser mehrfach ausgelöst, wenn man die Werte einzeln setzt. Wenn man aber SetBounds nimmt, wird der Event nur einmal ausgelöst.

Beispiel um das nach zu vollziehen:

Delphi-Quellcode:
procedure TFrmTest.Button1Click(Sender: TObject);
begin // FormResize wird mehrfach ausgelöst
  Top := Top - 10;
  Left := Left - 10;
  Width := Width + 10;
  Height := Height + 10;
end;

procedure TFrmTest.Button2Click(Sender: TObject);
begin // FormResize wird nur einmal ausgelöst
  Self.SetBounds(Left-10, Top-10, Width+10, Height+10);
end;

procedure TFrmTest.FormResize(Sender: TObject);
begin
  Memo1.Lines.Add(DateTimeToStr(Now)+': FormResize wurde ausgelöst');
end;
Das alles ist jetzt nichts großartiges, sondern eher das kleine 1x1 der Grundausbildung (ich hab noch keine UNI von innen gesehen und mir alles selber bei gebracht) aber wenn man schon Beispiele angibt, dann sollte man es meiner Meinung nach richtig machen und sich der Verantwortung bewusst sein: Hier lesen viele mit, die von den Beispielen etwas lernen wollen/sollen/müssen.

Das ist jetzt keine Meckerei, ich will auch niemandem zu nahe treten, also versteht mich nicht falsch. Mir ging es ja auch schon so. Ich hab Beispiele meines Codes veröffentlicht, und bin dafür gesteinigt worden. Aber nur dadurch lernt man dazu. Also positiv denken :thumb:


Alle Zeitangaben in WEZ +1. Es ist jetzt 04:19 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