Q:  How can I restore a window to its last state when I run it again?

A: Here is WindowRestorer - a window size and state restorer

DESCRIPTION:  Ever notice how professional programs seem to remember in what condition and location you left them and their child windows?  Ever notice how most RAD apps don't?  You can take that ragged edge off your program with this unit.  It Allows apps to save the location, size, and state of windows so that when the user reopens them, they will look as the user left them.

USE: Put WINRSTOR in the uses of clause of your main form and any forms that will be saving or restoring their own state, size, or location.  (If you will be doing all the saving and restoring using WinSaveChildren and WinRestoreChildren from the main form, you only need reference it in the main form's uses clause.)

In MainForm.Create, initialize the global WinRestorer object as follows (it's already declared in this file, but needs to be allocated): GlobalWinRestorer := TWinRestorer.create( Application, TRUE, WHATSAVE_ALL); Which is the same as: GlobalWinRestorer := TWinRestorer.create( Application, TRUE, [location, size, state]); Then, in MainForm.Destroy, deallocate the global WinRestorer object as follows: GlobalWinRestorer.free;
 

A good place to save a form's status is in the queryclose event or else attached to a button or menu item.  I usually create an item in the File Menu captioned 'Save &Workspace' which does: GlobalWinRestorer.SaveChildren(Self, [default]); And under main form's Close event I put: GlobalWinRestorer.SaveWin(Self, [WHATSAVE_ALL]);

I have tended to restore the children's status in their own show events like this: GlobalWinRestorer.RestoreWin(Self, [default]); though I am moving toward putting in the main form's show event: GlobalWinRestorer.RestoreWin(Self, [default]); GlobalWinRestorer.RestoreChildren(Self, [default]);

HINTS:  If you set TForm.Position to poScreenCenter or anything fancy, this unit won't do what you expect.  poDesigned seems to work fairly well.  I could have raised an exception if you try to set top and left of a poScreenCentere'd form, but then you have to be careful using WinRestoreChildren.  I opted not to check the position property and leave that up to individual developers.

unit WinRstor;

INTERFACE

USES SysUtils, Forms;

TYPE {=============================================================}

{------------------------------------------------------------------
Windows restorer object class and related types.
-------------------------------------------------------------------}
EWinRestorer = class( Exception);
TWhatSave = (default, size, location, state);
STWhatSave = set of TWhatSave;
TWinRestorer = class(TObject)
 protected
  mIniFile: string;
  mIniSect: string[80];
  mIsInitialized: boolean;
  mDefaultWhat: STWhatSave;
 public
  constructor Create( TheApp: TApplication;
    LocalDir: boolean; DefaultWhatSave: STWhatSave);
    {If localDir is true, ini dir is the app dir.  Else, ini dir is the windows dir.}
  procedure SaveWin(TheForm: TForm; What: STWhatSave);
  procedure SaveChildren(TheMDIForm: TForm; What: STWhatSave);
  procedure RestoreWin( TheForm: TForm; What: STWhatSave);
  procedure RestoreChildren(TheMDIForm: TForm; What: STWhatSave);
  property IniFileName: string  read mIniFile;
end;

CONST
  WHATSAVE_ALL = [size, location, state];

VAR
GlobalWinRestorer: TWinRestorer;

IMPLEMENTATION

Uses IniFiles;

constructor TWinRestorer.create;
var fname, path: string[100];
begin
  inherited create;
{Calculate ini file name}
  if default in DefaultWhatSave then
    raise EWinRestorer.create(
     'Attempt to initialize default window position paramaters with set ' +
     ' containing [default] item.  ' +
     'Default params may contain only members of [size, location, state].  ')
  else mDefaultWhat := DefaultWhatSave;
  fname := ChangeFileExt( ExtractFileName( TheApp.exeName), '.INI');
  if LocalDir then begin {parse out path and add to file name}
    path := ExtractFilePath(TheApp.exeName);
    if path[length(path)] <> '\' then
      path := path + '\';
    fname := path + fname;
  end;
{fill object fields}
  mIniFile := fname;
  mIniSect := 'WindowsRestorer';
{It'd be nice to write some notes to a section called [WinRestorer Notes]}
end;

procedure TWinRestorer.RestoreWin;
var FormNm, SectionNm: string[80];   ini: TIniFile;
  n,l,t,w,h: integer; {Left, Top Width, Height}
begin
  ini := TIniFile.create( mIniFile);
  TRY
    SectionNm := mIniSect;
    FormNm := TheForm.classname;
    if default in What then What := mDefaultWhat;
{Update Window State if Necessary}
    if state in What then
      n := ini.ReadInteger( SectionNm, FormNm + '_WindowState', 0);
      case  n of
        1:   TheForm.WindowState := wsMinimized;
        2:  TheForm.WindowState := wsNormal;
        3:   TheForm.WindowState := wsMaximized;
      end;
{Update Size and Location if necessary.}
    with TheForm do begin l:=left; t:=top; h:=height; w:=width; end; {Save current vals.}
    if size in What then begin
      w := ini.ReadInteger( SectionNm, FormNm + '_Width', w);
      h := ini.ReadInteger( SectionNm, FormNm + '_Height', h);
    end;
    if location in What then begin
      t := ini.ReadInteger( SectionNm, FormNm + '_Top', t);
      l := ini.ReadInteger( SectionNm, FormNm + '_Left', l);
    end;
    TheForm.SetBounds(l,t,w,h);
  FINALLY
    ini.free;
  END;
end;

procedure TWinRestorer.RestoreChildren;
var i: integer;
begin
  if TheMDIForm.formstyle <> fsMDIForm then
    raise EWinRestorer.create('Attempting to save window sizes of children for a non MDI parent window.')
  else
    for i := 0 to TheMDIForm.MDIChildCount - 1 do
      RestoreWin( TheMDIForm.MDIChildren[i], what);
end;

procedure TWinRestorer.SaveWin;
var FormNm, SectionNm: string[80];   w : STWhatsave; ini: TIniFile;
begin
  ini := TIniFile.create( mIniFile);
  TRY
    SectionNm := mIniSect;
    FormNm := TheForm.ClassName;
    if default in What then w := mDefaultWhat else w := mDefaultWhat;
    if size in w then begin
      ini.WriteInteger( SectionNm, FormNm + '_Width', TheForm.Width);
      ini.WriteInteger( SectionNm, FormNm + '_Height', TheForm.Height);
    end;
    if location in w then begin
      ini.WriteInteger( SectionNm, FormNm + '_Top', TheForm.Top);
      ini.WriteInteger( SectionNm, FormNm + '_Left', TheForm.Left);
    end;
    if state in w then
      case TheForm.WindowState of
        wsMinimized:   ini.WriteInteger( SectionNm, FormNm + '_WindowState', 1);
        wsNormal:     ini.WriteInteger( SectionNm, FormNm + '_WindowState', 2);
        wsMaximized:   ini.WriteInteger( SectionNm, FormNm + '_WindowState', 3);
      end;
  FINALLY
    ini.free;
  END;
end;

procedure TWinRestorer.SaveChildren;
var i: integer;
begin
  if TheMDIForm.formstyle <> fsMDIForm then
    raise EWinRestorer.create('Attempting to restore window sizes of children for a non MDI parent window.')
  else
    for i := 0 to TheMDIForm.MDIChildCount - 1 do
      SaveWin( TheMDIForm.MDIChildren[i], what);
end;

INITIALIZATION
END.