wsMaximized forms do not appear maximized
Solution 1
I Can reproduce with D7/Win7.
I don't use wsMaximized
at all (similar random problems as you describe).
Workaround: use OnActivate
-> ShowWindow(Handle, SW_MAXIMIZE)
e.g.:
procedure TForm1.FormActivate(Sender: TObject);
begin
// Maximize only once when the Form is first activated
if not FMaxsimized then
begin
FMaxsimized := True;
ShowWindow(Handle, SW_MAXIMIZE);
end;
end;
This method will not work during OnShow
.
Better Workaround: use ShowWindowAsync
during OnShow
or OnCreate
e.g:
procedure TForm1.FormCreate(Sender: TObject);
begin
ShowWindowAsync(Handle, SW_MAXIMIZE);
end;
This sets the show state of a window without waiting for the operation to complete.
Solution 2
I only tested the first reproduction case (with D7, D2007, XE2), and am able to duplicate the problem with D7 and D2007 but not with XE2.
The problem, as I see it, is that the label, having its font changed, requests its parent to re-align itself. This eventually leads to a SetWindowPos
call on the form (in TWinControl.AdjustSize
) with restored width/height even though the form is already maximized - which leads to the strange, behaviorally maximized but not visually maximized, form sitting on the screen.
I traced the code in D2007 and XE2 to be able to come up with what is different. The code in
TWinControl.AlignControls
is different between the two versions. What specifically matters is the last statement.
D2007:
procedure TWinControl.AlignControls(AControl: TControl; var Rect: TRect);
..
{ Apply any constraints }
if Showing then AdjustSize;
end;
XE2:
procedure TWinControl.AlignControls(AControl: TControl; var Rect: TRect);
..
// Apply any constraints
if FAutoSize and Showing then
DoAdjustSize;
end;
I hope this, somehow, helps you devising/deciding what workaround to use.
The workaround I could suggest (although I haven't tested it throughly) is to force show the form maximized early:
procedure TForm1.FormCreate(Sender: TObject);
var
wplc: TWindowPlacement;
begin
if not AutoScroll and (WindowState = wsMaximized) then begin
wplc.length := SizeOf(wplc);
GetWindowPlacement(Handle, @wplc);
wplc.rcNormalPosition.Right := wplc.rcNormalPosition.Left + Width;
wplc.rcNormalPosition.Bottom := wplc.rcNormalPosition.Top + Height;
wplc.showCmd := SW_MAXIMIZE;
SetWindowPlacement(Handle, @wplc);
end;
end;
The above works because it forces to set the focus to the edit control (OnEnter
event) before the VCL sets the visible flag for the form. In turn, the label's alignment request does not result with form size adjustment. Also, since, by the time VCL calls ShowWindow
the form's window is already visible, it doesn't cause the form to be shown in a restored state at any stage.
However, I don't know if it would help with different reproduction scenarios.
Finally, although I can see that the behavior is corrected in newer Delphi versions, I wouldn't consider this to be a bug in the VCL. In my opinion, user code should be responsible not to cause window adjustment while window showing state is changing. The course of action I'd take for the specific scenario would be to defer to modify label's font until the VCL is done displaying the form.
Solution 3
I don't think this is a bug in Delphi but rather a bug (or just odd behavior) in the Windows CreateWindow function. If you search for CreateWindow and WS_MAXIMIZE not working you'll find similarly very old threads and discussions from people calling CreateWindow or CreateWindowEx passing WS_MAXIMIZE in the style parameter and not seeing a maximized window when they run the application.
Excerpt from an old gamedev.net thread
the problem is that WS_MAXIMIZE apparently does not apply when using WS_OVERLAPPEDWINDOW. if you replace WS_OVERLAPPEDWINDOW with WS_POPUP you will get a maximized window. of course this may not apply to all versions of Windows, or even all versions of the Windows shell UI for that matter.
WS_OVERLAPPEDWINDOW is MS's old default window "type" and they apparently coded CreateWindow/Ex to ignore certain styles, thinking that ShowWindow would be called with SW_SHOWDEFAULT, which causes the window to be displayed according to the CreateProcess startup info parms. this ultimately gives the user the control of how an app's main window would be displayed by using the shell's shortcut settings.
The workaround is just to call ShowWindow. It should work in Delphi, too:
procedure TForm1.FormShow(Sender: TObject);
begin
ShowWindow(Handle, SW_MAXIMIZE);
end;
mistertodd
Any code is public domain. No attribution required. జ్ఞా <sup>🕗</sup>🕗 Yes, i do write i with a lowercase i. The Meta Stackexchange answer that I am most proud of
Updated on July 14, 2022Comments
-
mistertodd almost 2 years
Setting a form to
WindowState = wsMaximized
will sometimes cause the form to be maximized but not:Long-time bug: this is a question I first asked in the Borland newsgroups in 2003:
and then again in 2006:
and then again in 2008:
Someone asked it on the Embarcadero forums in 2012:
Now it's time to port the 18 year old bug to Stackoverflow. Maybe someone's finally figured out a workaround.
Steps to reproduce:
My posts contained half a dozen failure modes, but the easiest is:
-
Drop a
Label
and anEdit
on a form: -
Add an
OnEnter
event for theTEdit
:procedure TForm1.Edit1Enter(Sender: TObject); begin Label1.Font.Style := Label1.Font.Style + [fsBold]; end;
-
and set the form:
-
WindowState
to wsMaximized -
AutoScroll
to False
-
And bazinga, fails.
One of the other set of steps from the 2008 post:
- Create a new app and a form.
- Set the form to maximized (WindowState = wsMaximized) at design time.
- Drop a ListView control on the form
-
During OnShow, add 20 empty items to the list view:
procedure TForm1.FormShow(Sender: TObject); var i: Integer; begin for i := 1 to 20 do ListView1.Items.Add; end;
Set the form's AutoScroll property to false (AutoScroll = False) at design time
Of course what I'm not after is "fixed in version
n
of RadStudio. Just use that". I'm looking for an actual fix (if there is one); which could include quoting relevant changes to the VCL source when CodeGear finally did fix it. (If it is even fixed).Note: Changing
Position
from poDesigned to anything else doesn't fix it.Workaround
A horrible, ugly, awful, disgusting, workaround I had been using was to start a timer during
OnShow
, and then when the timer fires, maximize the form:procedure TForm1.tmrVclMaximizeHackTimer(Sender: TObject); begin Self.WindowState := wsMaximized; end;
I later improved this hack to post a message during
OnShow
; which is essentially the same as a timer message, without having to use a timer:const WM_MaximizeWindow = WM_APP + $03; procedure TForm1.FormShow(Sender: TObject); begin if (Self.WindowState = wsMaximized) then begin Self.WindowState := wsNormal; PostMessage(Self.Handle, WM_MaximizeWindow , 0, 0); end; end; private procedure WMMaximizeWindow(var Message: TMessage); message WM_MaximizeWindow; procedure TForm1.WMMaximizeWindow(var Message: TMessage); begin Self.WindowState := wsMaximized; end;
Sometimes I invent the
OnAfterShow
event that Delphi never did:const WM_AfterShow = WM_APP + $02; procedure TForm1.FormShow(Sender: TObject); begin PostMessage(Self.Handle, WM_AfterShow, 0, 0); if (Self.WindowState = wsMaximized) then begin Self.WindowState := wsNormal; FMaximizeNeeded := True; end; end; private procedure WMAfterShow(var Message: TMessage); message WM_AfterShow; procedure TForm1.WMAfterShow(var Message: TMessage); begin if FMaximizeNeeded then begin FMaximizeNeeded := False; Self.WindowState := wsMaximized; end; end;
But no hacks are better than hacks.
-