2005. január 26., szerda

Save your window size and position


Problem/Question/Abstract:

It's often useful to remember the size and state of your program's window (or sometimes of dialogue boxes) between executions. This article discusses how.

Answer:

The method we're going to use is to save the position in the registry.

First of all, decide where you're going to keep the information. It's customary for apps to place information that varies between users in

HKEY_CURRENT_USER\Software\MyCompany\MyProgram\X.X

(X.X is the version number of the program). We'll use such a key in this article.

You can save the window's current position and size when the program is closing down - the OnDestroy form event handler is a good place for this. The program then restores it's position from the registry (if it's been written yet) when opening - we use the form's OnCreate handler for that code.

There are complications when saving and restoring the window state because of when the window is minimised, Delphi doesn't minimise the form - it hides it and displays the Application window in the taskbar. The method I've used causes a previously minimised window to flash on-screen briefly. I'd welcome ideas on any alternative approaches. (This has now been fixed -- see the component available for download).

Another complication is that when a window is maximised Delphi updates the Width, Height, Left and Top properties of the form to the window's maximised size and position. This means that closing a maximised window stores the maximised size in the registry. When the program is run again it appears maximised, but when the user restores it they expect it to go to the previous normal size and position, but if we reloaded the Left, Top, Height and Width properties, the form won't shrink when restored. We get round this by using the Windows API to get the non-maximised size.

Here's the code - the comments should explain what's happening.

const
  CRegKey = 'Software\Demos\WdwStateDemo\1.0';

  // Helper function to read registry values, and deal with
  // cases where no values exist

function ReadIntFromReg(Reg: TRegistry; Name: string;
  Def: Integer): Integer;
{Reads integer with given name from registry and returns it
If no such value exists, returns Def default value}
begin
  if Reg.ValueExists(Name) then
    Result := Reg.ReadInteger(Name)
  else
    Result := Def;
end;

procedure TForm1.FormDestroy(Sender: TObject);
var
  Reg: TRegistry; // the registry
  State: Integer; // state of wdw
  Pl: TWindowPlacement; // used for API call
  R: TRect; // used for wdw pos
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 maximised, which is not what we want here}
  Pl.Length := SizeOf(TWindowPlacement);
  GetWindowPlacement(Self.Handle, @Pl);
  R := Pl.rcNormalPosition;
  Reg := TRegistry.Create;
  try
    // Open required key - and create it if it doesn't exist
    Reg.RootKey := HKEY_CURRENT_USER;
    Reg.OpenKey(CRegKey, True);
    // Write window size and position
    Reg.WriteInteger('Width', R.Right - R.Left);
    Reg.WriteInteger('Height', R.Bottom - R.Top);
    Reg.WriteInteger('Left', R.Left);
    Reg.WriteInteger('Top', R.Top);
    // Write out state of window
    {Record window state (maximised, minimised or normal)
    - special case when minimized since form window is simply
    hidden when minimised, and application window is actually
    the one minimised - so we check to see if application
    window *is* minimized and act accordingly}
    if IsIconic(Application.Handle) then
      {minimized - write that state}
      State := Ord(wsMinimized)
    else
      {not mimimized - we can rely on window state of form}
      State := Ord(Self.WindowState);
    Reg.WriteInteger('State', State);
  finally
    Reg.Free;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  Reg: TRegistry; // the registry
  State: Integer; // state of wdw
begin
  Reg := TRegistry.Create;
  try
    // Open required key - and exit if it doesn't exist
    Reg.RootKey := HKEY_CURRENT_USER;
    if not Reg.OpenKey(CRegKey, False) then
      Exit;
    // Read the window size and position
    // - designed form sizes are defaults
    Self.Width := ReadIntFromReg(Reg, 'Width', Self.Width);
    Self.Height := ReadIntFromReg(Reg, 'Height', Self.Height);
    Self.Left := ReadIntFromReg(Reg, 'Left', Self.Left);
    Self.Top := ReadIntFromReg(Reg, 'Top', Self.Top);
    // Now get window state and restore
    State := ReadIntFromReg(Reg, 'State', Ord(wsNormal));
    {check if window was minimised - we have special
    processing for minimized state since Delphi doesn't
    minimize windows - it uses application window
    instead}
    if State = Ord(wsMinimized) then
    begin
      {we need to set visible true else form won't restore
      properly - but this causes a brief display of form
      any ideas on how to stop this?}
      Self.Visible := True;
      Application.Minimize;
    end
    else
      Self.WindowState := TWindowState(State);
  finally
    Reg.Free;
  end;
end;

A component that wraps up all this functionality on behalf of the form it lives on is available for download. There's also a sister component included that works with ini files rather than the registry.


Component Download: http://www.delphidabbler.com/download.php?file=pjwdwstate.zip

Nincsenek megjegyzések:

Megjegyzés küldése