Dynamically assigning form size before maximize loses assigned values

Viewed 723

I have an application which always starts initially maximized. This consists of putting Self.WindowState := wsMaximized; in the OnCreate of the main form.

Just before that, I'm assigning what should be the default dimensions of the main form, if the user were to change the window state to wsNormal.

So, in short, the main form's OnCreate handler looks something like:

procedure TfrmMain.FormCreate(Sender: TObject);
begin
  Width:= 1300;
  Height:= 800;
  WindowState:= wsMaximized;
end;

Theoretically, I could assign these dimensions in design-time, and that does what I need. However, due to the size of my screen, and thus the IDE, the whole form is not visible at one glance without scrolling. In design, I keep the form size small, so I can see everything. But in runtime, I need to assign these default dimensions, and then maximize it by default. When the user changes the window state out of maximized, I expect it to go to those dimensions I dynamically assigned.

The issue is that it seems to lose those dimensions after maximizing the form, and it reverts back to whatever values were in design-time. If I comment out the line WindowState:= wsMaximized; then it shows the form in the desired default dimensions. However, maximizing it seems to overwrite and ignore these values I had just assigned before it.

How can I create and show my main form maximized by default, while at the same time dynamically assigning the default size, without my assigned values getting lost?

3 Answers

(Confirmed with 10.3.3.) The exact origin of this problem I cannot pinpoint, but a reasonable cause would be that during the constructor the form component is being read and that previous sizes seem to be explicitly backed up:

procedure TControl.SetWidth(Value: Integer);
begin
  SetBounds(FLeft, FTop, Value, FHeight);
  Include(FScalingFlags, sfWidth);
  if csReading in ComponentState then
    FExplicitWidth := FWidth;
end;

A possible solution is to set the desired sizes in the OnCreate event, like you are doing now, but postpone setting the desired WindowsState until the OnShow event.

procedure TForm1.FormCreate(Sender: TObject);
begin
  Width := 1300;
  Height := 800;
end;

procedure TForm1.FormShow(Sender: TObject);
begin
  WindowState := wsMaximized;
end;

Of course, you probably should prevent consecutive calls by using a one-off mechanism. Please take a look at wsMaximized forms do not appear maximized too.

Apparently, the VCL does not store the explicit intermediate size (in some Delphi versions anyway) but seems to merge the change with that of the maximization when the form is actually shown.

Like Sertac Akyuz quite correctly suggested, you can use SetWindowPlacement to bypass this VCL interference:

procedure TForm1.FormCreate(Sender: TObject);
var
  WindowPlacement: TWindowPlacement;
begin
  GetWindowPlacement(Handle, WindowPlacement);
  WindowPlacement.rcNormalPosition := Bounds(Left, Top, 1300, 800);
  WindowPlacement.showCmd := SW_SHOWMAXIMIZED;
  SetWindowPlacement(Handle, WindowPlacement);
end;

You must set form size on FormActivate:

procedure TfrmMain.FormActivate(Sender: TObject);
begin
  if Tag = 0 then
  begin
    // Top := 100;
    // Left := 100;
    Width:= 1300;
    Height:= 800;
    WindowState:= wsMaximized;
    Tag := 1;
  end;
end;
Related