問題点
フォームのBorderStyleがbsNoneのとき,設計時にWindowsStateをwsMaximizedすると
実行時に最大化を解除してもWidthとHeightで指定したサイズにならない.
対策
設計時はフォームを最大化せず,実行中最初にフォームを表すときに最大化するとこの問題を回避できます.
type
TForm1 = class(TForm)
(※中略)
private
FFormInitialized: boolean;
(※中略)
end;
procedure TForm1.FormShow(Sender: TObject);
begin
if not FFormInitialized then
begin
WindowsState := wsMaximized;
FFormInitialized := True;
end;
end;
ただしこの場合,Positionの値はpoDesignedとなり,最大化を解除すると,
- PositionがpoDefault,poDefaultPosOnlyの場合はフォームの左上座標画面の左上に来てしまう.
このため,タスクバーなどのアプリケーションバーが画面の上又は左に存在するとフォームの一部又は全部が隠れてしまう.
- それ以外の場合は常に設計時にLeft, Topで指定した座標に表示される.
poScreenCenter等の指定は無視されてしまう.
この対策は自分で表示位置を修正するしかないようです.
アプリケーションバーと重なっているかどうかを調べる方法
アプリケーションバーについてひとつひとつ調べる必要はありません.SystemParametersInfo APIでアプリケーションバーを除いた領域(ここでは作業領域と呼びます)を簡単に取得できるからです.現在のフォーム領域がこの領域をはみ出ているかどうかを調べれば判定できます.
重なっている場合に重ならないように移動する方法
単純に左にはみ出ていればその分右に移動,上にはみ出ていればその分下に移動.右と下も同様です.ただし左右又は上下ともにはみ出ている場合,つまりフォームの幅や高さが作業領域の幅や高さより大きい場合は幅や高さを縮めるか片方だけ適用します.以下にコードを晒します(※このコードでは幅や高さの縮小はしていません).
コード
※宣言部
function CenterRectOfRect(const aRect, MotherRect: TRect): TRect;
function DesktopWorkRect: TRect;
function WidthOfRect(const aRect: TRect): integer;
function HeightOfRect(const aRect: TRect): integer;
procedure MoveFormToPosition(Form: TCustomForm; Position: TPosition = poDesigned);
※実装部
{★指定された領域と同じサイズで別の領域の中央になる領域を返します.}
function CenterRectOfRect(const aRect, MotherRect: TRect): TRect;
begin
Result.Left := MotherRect.Left + (WidthOfRect(MotherRect) - WidthOfRect(aRect)) div 2;
Result.Right := aRect.Right + (Result.Left - aRect.Left);
Result.Top := MotherRect.Top + (HeightOfRect(MotherRect) - HeightOfRect(aRect)) div 2;
Result.Bottom := aRect.Bottom + (Result.Top - aRect.Top);
end;
{★アプリケーションバーを除いた領域を返す
(※マルチモニターの場合はプライマリモニターの領域を返します)}
function DesktopWorkRect: TRect;
begin
Windows.SystemParametersInfo(SPI_GETWORKAREA, 0, @Result, 0)
end;
{★領域の幅を返す.}
function WidthOfRect(const aRect: TRect): integer;
begin
Result := abs(aRect.Right - aRect.Left);
end;
{★領域の高さを返す.}
function HeightOfRect(const aRect: TRect): integer;
begin
Result := abs(aRect.Bottom - aRect.Top);
end;
{★フォームの位置をPositionで指定した場所に移動します.}
procedure MoveFormToPosition(Form: TCustomForm; Position: TPosition);
var
aDesktopWorkRect, aBoundsRect: TRect;
NeedToMoveOffsetX, NeedToMoveOffsetY: integer;
begin
aDesktopWorkRect := DesktopWorkRect;
aBoundsRect := Form.BoundsRect;
case Position of
poDefault, poDefaultPosOnly:
begin
{ クライアント領域からはみ出しているなら収まるように移動します.}
if aBoundsRect.Left < aDesktopWorkRect.Left then
NeedToMoveOffsetX := aDesktopWorkRect.Left - aBoundsRect.Left
else if aBoundsRect.Right > aDesktopWorkRect.Right then
NeedToMoveOffsetX := aDesktopWorkRect.Right - aBoundsRect.Right
else
NeedToMoveOffsetX := 0;
if aBoundsRect.Top < aDesktopWorkRect.Top then
NeedToMoveOffsetY := aDesktopWorkRect.Top - aBoundsRect.Top
else if aBoundsRect.Bottom > aDesktopWorkRect.Bottom then
NeedToMoveOffsetY := aDesktopWorkRect.Bottom - aBoundsRect.Bottom
else
NeedToMoveOffsetY := 0;
if (NeedToMoveOffsetX <> 0) or (NeedToMoveOffsetY <> 0) then
begin
Windows.OffsetRect(aBoundsRect, NeedToMoveOffsetX , NeedToMoveOffsetY);
Form.BoundsRect := aBoundsRect;
end;
end;
poScreenCenter:
Form.BoundsRect := CenterRectOfRect(aBoundsRect, Bounds(0, 0, Screen.Width, Screen.Height));
poDesktopCenter:
Form.BoundsRect := CenterRectOfRect(aBoundsRect, DesktopWorkRect);
poMainFormCenter:
if Form <> Application.MainForm then
Form.BoundsRect := CenterRectOfRect(aBoundsRect, Application.MainForm.BoundsRect)
else
MoveFormToPosition(Form, poScreenCenter);
poOwnerFormCenter:
if Form.Owner is TCustomForm then
Form.BoundsRect := CenterRectOfRect(aBoundsRect, (Form.Owner as TCustomForm).BoundsRect)
else
MoveFormToPosition(Form, poMainFormCenter);
end;
end;
使い方の例
procedure TForm1.FormShow(Sender: TObject);
begin
MoveFormToPosition(Form, poDefaultPosOnly);
end;