- 追加された行はこの色です。
- 削除された行はこの色です。
[[戻る>DoldoWorkz#computerspeciality]]
*Delphiでウインドウの最大化時の大きさをコントロール(マルチモニタ対応) [#b6d19f99]
#hr
#contents
#hr
**参考にした情報 [#j0a87610]
この内容はDelphi-ML「[Delphi:90115] ウインドウを最大化したまま最大化サイズを調節する」で付いたレスを参考にしています。
教えてくださる皆様、感謝感謝です。
**参考にしたWebページ [#u10e58ac]
-[[MSDN Japan-GetMonitorInfo:http://msdn.microsoft.com/library/ja/default.asp?url=/library/ja/jpgdi/html/_win32_GetMonitorInfo.asp]]~
GetMonitorInfoの情報があります。~
-[[MONITORINFO構造体-WinAPI Database for VB Programmer:http://www.winapi-database.com/Struct/MONITORINFO.html]]~
MONITORINFO 構造体の情報があります。~
**GetMonitorInfoでモニタに関する情報を取得する [#q67f6944]
モニタの範囲を知るためにはWindows APIの「GetMonitorInfo」を使う。~
このAPIはマルチモニタ対応のAPIで、ディスプレイモニタのハンドルを渡してあげる事によって、そのディスプレイモニタに関する情報を取得する事が出来ます。~
~
情報はMONITORINFO 構造体によって返され、以下のような情報が帰ってきます。~
~
&color(red){''モニタの範囲''};~
MonitorInfo.rcMonitor.Bottom~
MonitorInfo.rcMonitor.Top~
MonitorInfo.rcMonitor.Left~
MonitorInfo.rcMonitor.Right~
~
&color(Green){''モニタの有効面の範囲''};~
MonitorInfo.rcWork.Bottom~
MonitorInfo.rcWork.Top~
MonitorInfo.rcWork.Left~
MonitorInfo.rcWork.Right~
~
以下の例では赤い四角がモニタの範囲、緑色の範囲がモニタの有効面の範囲になってます。~
以下の例ではタスクバーの分が有効面から除外されています。~
これは、ウインドウを最大化したときに使える範囲=有効面だからです。~
なので、他のソフトでデスクトップを占有するタイプのアプリケーションがある場合はその分も
有効面から除外されます~
#ref(Monitor1.jpg)
~
取得出来る値は、プライマリモニタの左上を原点として数値が決まります。~
2台目以降のモニタの位置はある程度好きなように置ける様です。~
プライマリモニタより左・上のモニタは取得出来る値が当然マイナスで帰って来ます。~
#ref(Monitor2.jpg)
※ぜんぜん関係ない話ですが、上の図はExcelで作っている物を、セル範囲のコピーってそのままペイント等に画像として貼り付けしてます。~
知らなかったです…なんとなく「出来るかな?」と思ってやって出来てびっくり。~
~
Delphiのコードだとこんな感じです
var
FMonitorInfo: TMonitorInfo;
begin
ZeroMemory(@FMonitorInfo, sizeof(FMonitorInfo));
FMonitorInfo.cbSize := SizeOf(FMonitorInfo);
GetMonitorInfo(FTargetForm.Monitor.Handle, @FMonitorInfo);
**作っていて気になった事柄 [#dc8f5264]
procedure WMSysCommand(var Message: TWMSysCommand); message WM_SYSCOMMAND;
の関数の中で使われている「msg.MinMaxInfo.ptMaxPosition.X」「msg.MinMaxInfo.ptMaxPosition.Y」なのですが、よくよく調べてみるとココに入れる値は~
// GetMonitorInfo,Self.Top,Self.Leftで帰ってくる値は
// プライマリモニタの左上を原点(0,0)とした値が帰ってくるが、
// msg.MinMaxInfo.ptMaxPositionは
// BorderStyleがbsSizeableおよびbsSingleの場合のみ
// 各種モニタごとの有効面の左上を原点(0,0)
// とした座標を指定しなければいけない
// それ以外のBorderStyleでは
// 各種モニタごとの左上を原点(0,0)とした座標を指定する。
という事らしい。~
ウインドウのモードによって変わるなんて…あああ大混乱する…。~
**作ってみたコード [#o031755d]
マルチディスプレイ対応です。~
この内容はDelphi-ML「[Delphi:90115] ウインドウを最大化したまま最大化サイズを調節する」で付いたレスを参考にしています。~
教えてくださる皆様、感謝感謝です。~
&color(red){''2008/1/24修正 ウインドウがbsSizeable・bsSingle以外のモードの場合が動きおかしかったのを修正''};
ダウンロード
#ref(MultiMonitorParam20080127.ZIP)
#ref(MultiMonitorParam080127.ZIP)
{*****************************************************************************}
{ }
{ MultiMonitorParam }
{ }
{ Copyright (c) 2007 Moon Doldo }
{ }
{この内容はDelphi-ML }
{[Delphi:90115] ウインドウを最大化したまま最大化サイズを調節する }
{で付いたレスを参考にしています。 }
{ }
{*****************************************************************************}
unit MultiMonitorParam;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Math, MultiMon;
type
// MonitorInfo保持クラス
TMonitorInfoParam = class
private
FMonitorInfo : TMonitorInfo;
FDisplayMonitorInfo: TMonitorInfo;
FTargetForm: TForm;
procedure SetTargetForm(const Value: TForm);
protected
// MonitorInfoの取得
procedure GetMonitorInfoParam;
public
// 対象のウインドウを設定
property TargetForm: TForm read FTargetForm write SetTargetForm;
function MonitorInfo: TMonitorInfo;
function DisplayMonitorInfo: TMonitorInfo;
// モニタの数を取得
function MonitorCount: Integer;
// モニタ番号の取得
function MonitorNum: Integer;
end;
// 最大化の管理クラス
TMultiMonitorMaxInfoCalc = class(TMonitorInfoParam)
private
FMaxSizeY: Integer;
FMaxSizeX: Integer;
FMaxPositionY: Integer;
FMaxPositionX: Integer;
FMaxAlign: TAlign;
FSetWMGetMinMaxInfo2: Boolean;
procedure SetMaxSizeX(const Value: Integer);
procedure SetMaxSizeY(const Value: Integer);
procedure SetMaxPositionX(const Value: Integer);
procedure SetMaxPositionY(const Value: Integer);
procedure SetMaxAlign(const Value: TAlign);
protected
// フォームの大きさと位置の制限
procedure WMClientGetMinMaxInfo(var msg : TWMGetMinMaxInfo);
procedure WMCustomGetMinMaxInfo(var msg : TWMGetMinMaxInfo);
procedure WMTopGetMinMaxInfo(var msg : TWMGetMinMaxInfo);
procedure WMBottomGetMinMaxInfo(var msg : TWMGetMinMaxInfo);
procedure WMLeftGetMinMaxInfo(var msg : TWMGetMinMaxInfo);
procedure WMRightGetMinMaxInfo(var msg : TWMGetMinMaxInfo);
public
//TObjectのコンストラクタ定義
constructor Create;
//デストラクタ定義
destructor Destroy; override;
// フォームの大きさと位置の制限
procedure WMGetMinMaxInfo(var msg : TWMGetMinMaxInfo);
// フォームの大きさと位置の制限(既に最大化している場合)
procedure WMGetMinMaxInfo2;
// 通常の状態のフォームを適切な位置へ移動する
procedure SetFormSafePosition;
published
// 設定したいウインドウサイズを指定する
// alLeft, alRight, alCustomの場合のみ実行される
property MaxSizeX: Integer read FMaxSizeX write SetMaxSizeX;
// alTop, alBottom, alCustomの場合のみ実行される
property MaxSizeY: Integer read FMaxSizeY write SetMaxSizeY;
// 設定したい位置を指定する(各種モニタの有効面の左上を原点(0,0))
// alCustomの場合のみ実行される
property MaxPositionX: Integer read FMaxPositionX write SetMaxPositionX;
property MaxPositionY: Integer read FMaxPositionY write SetMaxPositionY;
// 最大化の際に、モニタ内でコントロールを揃える方法を指定します。
property MaxAlign: TAlign read FMaxAlign write SetMaxAlign;
end;
implementation
{ TMultiMonitorMaxInfoCalc }
constructor TMultiMonitorMaxInfoCalc.Create;
begin
FMaxAlign := alCustom;
FMaxSizeY := 0;
FMaxSizeX := 0;
FMaxPositionY := 0;
FMaxPositionX := 0;
FSetWMGetMinMaxInfo2 := False;
end;
destructor TMultiMonitorMaxInfoCalc.Destroy;
begin
inherited;
end;
procedure TMultiMonitorMaxInfoCalc.SetFormSafePosition;
var
WkMonitorInfo : TMonitorInfo;
WkMonitorInfoWidth, WkMonitorInfoHeight: Integer;
WkTargetFormRight, WkTargetFormBottom: Integer;
WkLeft, WkTop: Integer;
begin
if FTargetForm.WindowState = wsNormal then
begin
// マルチモニタ対応の有効面の取得
WkMonitorInfo := Self.MonitorInfo;
WkLeft := FTargetForm.Left;
WkTop := FTargetForm.Top;
// 左上が有効面からはみ出ていたら直す
if WkLeft < WkMonitorInfo.rcWork.Left then
begin
WkLeft := WkMonitorInfo.rcWork.Left;
end;
if WkTop < WkMonitorInfo.rcWork.Top then
begin
WkTop := WkMonitorInfo.rcWork.Top;
end;
// 右下が有効面からはみ出ていたら直す
// ただし、有効面のよりフォームが小さい場合のみ行う(左上を優先にするため)
WkMonitorInfoWidth :=
WkMonitorInfo.rcWork.Right - WkMonitorInfo.rcWork.Left;
if FTargetForm.Width <= WkMonitorInfoWidth then
begin
WkTargetFormRight := WkLeft + FTargetForm.Width;
if WkTargetFormRight > WkMonitorInfo.rcWork.Right then
begin
WkLeft := WkMonitorInfo.rcWork.Right - FTargetForm.Width;
end;
end;
WkMonitorInfoHeight :=
WkMonitorInfo.rcWork.Bottom - WkMonitorInfo.rcWork.Top;
if FTargetForm.Height <= WkMonitorInfoHeight then
begin
WkTargetFormBottom := WkTop + FTargetForm.Height;
if WkTargetFormBottom > WkMonitorInfo.rcWork.Bottom then
begin
WkTop := WkMonitorInfo.rcWork.Bottom - FTargetForm.Height;
end;
end;
FTargetForm.Left := WkLeft;
FTargetForm.Top := WkTop;
end;
end;
procedure TMultiMonitorMaxInfoCalc.SetMaxAlign(const Value: TAlign);
begin
FMaxAlign := Value;
WMGetMinMaxInfo2;
end;
procedure TMultiMonitorMaxInfoCalc.SetMaxPositionX(const Value: Integer);
begin
FMaxPositionX := Value;
end;
procedure TMultiMonitorMaxInfoCalc.SetMaxPositionY(const Value: Integer);
begin
FMaxPositionY := Value;
end;
procedure TMultiMonitorMaxInfoCalc.SetMaxSizeX(const Value: Integer);
begin
FMaxSizeX := Value;
end;
procedure TMultiMonitorMaxInfoCalc.SetMaxSizeY(const Value: Integer);
begin
FMaxSizeY := Value;
end;
procedure TMultiMonitorMaxInfoCalc.WMBottomGetMinMaxInfo(
var msg: TWMGetMinMaxInfo);
var
WkMonitorInfo : TMonitorInfo;
WkSizeX, WkSizeY: Integer;
WkPositionX, WkPositionY: Integer;
begin
// マルチモニタ対応の有効面の取得
WkMonitorInfo := Self.MonitorInfo;
WkSizeX :=
WkMonitorInfo.rcWork.Right - WkMonitorInfo.rcWork.Left;
WkSizeY :=
Min(FMaxSizeY,
WkMonitorInfo.rcWork.Bottom - WkMonitorInfo.rcWork.Top);
WkPositionX := WkMonitorInfo.rcWork.Left;
WkPositionY := WkMonitorInfo.rcWork.Bottom - WkSizeY;
msg.MinMaxInfo.ptMaxSize.X := WkSizeX;
msg.MinMaxInfo.ptMaxSize.Y := WkSizeY;
// GetMonitorInfo,Self.Top,Self.Leftで帰ってくる値は
// プライマリモニタの左上を原点(0,0)とした値が帰ってくるが、
// msg.MinMaxInfo.ptMaxPositionは
// BorderStyleがbsSizeableおよびbsSingleの場合のみ
// 各種モニタごとの有効面の左上を原点(0,0)
// とした座標を指定しなければいけない
// それ以外のBorderStyleでは
// 各種モニタごとの左上を原点(0,0)とした座標を指定する。
if (TargetForm.BorderStyle = bsSizeable) or
(TargetForm.BorderStyle = bsSingle) or
(FSetWMGetMinMaxInfo2 = True) then
begin
WkPositionX := WkPositionX - WkMonitorInfo.rcWork.Left;
WkPositionY := WkPositionY - WkMonitorInfo.rcWork.Top;
FSetWMGetMinMaxInfo2 := False;
end
else begin
WkPositionX := WkPositionX - WkMonitorInfo.rcMonitor.Left;
WkPositionY := WkPositionY - WkMonitorInfo.rcMonitor.Top;
end;
msg.MinMaxInfo.ptMaxPosition.X :=
WkPositionX;
msg.MinMaxInfo.ptMaxPosition.Y :=
WkPositionY;
end;
procedure TMultiMonitorMaxInfoCalc.WMCustomGetMinMaxInfo(
var msg: TWMGetMinMaxInfo);
var
WkMonitorInfo : TMonitorInfo;
WkSizeX, WkSizeY: Integer;
WkPositionX, WkPositionY: Integer;
begin
// マルチモニタ対応の有効面の取得
WkMonitorInfo := Self.MonitorInfo;
// 位置の値を保持
// プライマリモニタの左上を原点(0,0)とした値に直す
WkPositionX :=
FMaxPositionX + WkMonitorInfo.rcWork.Left;
WkPositionY :=
FMaxPositionY + WkMonitorInfo.rcWork.Top;
// 有効面から左上にはみ出ている場合は直す
if WkPositionX < WkMonitorInfo.rcWork.Left then
begin
WkPositionX := WkMonitorInfo.rcWork.Left;
end;
if WkPositionY < WkMonitorInfo.rcWork.Top then
begin
WkPositionY := WkMonitorInfo.rcWork.Top;
end;
// 有効面以上の大きさにならないように大きさを修正する
WkSizeX :=
Min(FMaxSizeX,
WkMonitorInfo.rcWork.Right - WkMonitorInfo.rcWork.Left - FMaxPositionX);
WkSizeY :=
Min(FMaxSizeY,
WkMonitorInfo.rcWork.Bottom - WkMonitorInfo.rcWork.Top - FMaxPositionY);
msg.MinMaxInfo.ptMaxSize.X := WkSizeX;
msg.MinMaxInfo.ptMaxSize.Y := WkSizeY;
// GetMonitorInfo,Self.Top,Self.Leftで帰ってくる値は
// プライマリモニタの左上を原点(0,0)とした値が帰ってくるが、
// msg.MinMaxInfo.ptMaxPositionは
// BorderStyleがbsSizeableおよびbsSingleの場合のみ
// 各種モニタごとの有効面の左上を原点(0,0)
// とした座標を指定しなければいけない
// それ以外のBorderStyleでは
// 各種モニタごとの左上を原点(0,0)とした座標を指定する。
if (TargetForm.BorderStyle = bsSizeable) or
(TargetForm.BorderStyle = bsSingle) or
(FSetWMGetMinMaxInfo2 = True) then
begin
WkPositionX := WkPositionX - WkMonitorInfo.rcWork.Left;
WkPositionY := WkPositionY - WkMonitorInfo.rcWork.Top;
FSetWMGetMinMaxInfo2 := False;
end
else begin
WkPositionX := WkPositionX - WkMonitorInfo.rcMonitor.Left;
WkPositionY := WkPositionY - WkMonitorInfo.rcMonitor.Top;
end;
msg.MinMaxInfo.ptMaxPosition.X :=
WkPositionX;
msg.MinMaxInfo.ptMaxPosition.Y :=
WkPositionY;
end;
procedure TMultiMonitorMaxInfoCalc.WMGetMinMaxInfo(
var msg: TWMGetMinMaxInfo);
var
WkMonitorInfo : TMonitorInfo;
WkSizeX, WkSizeY: Integer;
WkPositionX, WkPositionY: Integer;
WkPositionRight, WkPositionBottom: Integer;
begin
case FMaxAlign of
alNone:
begin
// 何もしないデフォルトの動作
Exit;
end;
alClient:
begin
WMClientGetMinMaxInfo(msg);
end;
alCustom:
begin
WMCustomGetMinMaxInfo(msg);
end;
alTop:
begin
WMTopGetMinMaxInfo(msg);
end;
alBottom:
begin
WMBottomGetMinMaxInfo(msg);
end;
alLeft:
begin
WMLeftGetMinMaxInfo(msg);
end;
alRight:
begin
WMRightGetMinMaxInfo(msg);
end;
else
;
end;
end;
procedure TMultiMonitorMaxInfoCalc.WMGetMinMaxInfo2;
var
WkMonitorInfo : TMonitorInfo;
WkWindowPlacement, WkSaveWindowPlacement : TWindowPlacement;
WkSmCxframe, WkSmCyframe: Integer;
WkSizeX, WkSizeY: Integer;
WkPositionX, WkPositionY: Integer;
begin
// 最大化の時のみ実行される
if IsZoomed(FTargetForm.Handle) then
begin
if (FMaxAlign <> alNone) and (FMaxAlign <> alClient) then
begin
// ウィンドウの配置情報を保持
ZeroMemory(@WkSaveWindowPlacement, sizeof(WkSaveWindowPlacement));
WkSaveWindowPlacement.length := sizeof(WkSaveWindowPlacement);
GetWindowPlacement(FTargetForm.Handle, @WkSaveWindowPlacement);
// フォームの最大化スタイルを無理やり消しちゃう
SetWindowLong(
FTargetForm.Handle,
GWL_STYLE,
GetWindowLong(FTargetForm.Handle, GWL_STYLE) and not WS_MAXIMIZE);
// さらに最大化
// ただ、この方法だとBorderStyleがbsSizeable・bsSingle以外の場合でも
// WMGetMinMaxInfoがbsSizeable・bsSingleとして認識してしまうため、
// その対策のためのフラグを立てて
// 必ずBorderStyleがbsSizeable・bsSingleの扱いで動作させる
// もっとスマートな方法ないのかな・・・
FSetWMGetMinMaxInfo2 := True;
ShowWindow(FTargetForm.Handle, SW_MAXIMIZE);
// 新しい位置情報を取得
ZeroMemory(@WkWindowPlacement, sizeof(WkWindowPlacement));
WkWindowPlacement.length := sizeof(WkWindowPlacement);
GetWindowPlacement(FTargetForm.Handle, @WkWindowPlacement);
// リストア時の情報のみ上書き
WkWindowPlacement.rcNormalPosition := WkSaveWindowPlacement.rcNormalPosition;
//保持していたウィンドウの配置情報を戻す
SetWindowPlacement(FTargetForm.Handle, @WkWindowPlacement);
end
else begin
// 上記のコードで全部まかなえると思ったら通常の最大化だけおかしくなるので
// 最大化のコードだけ以下のものを使う
// 通常の最大化
// 通常、Windowsでは最大化した際にウインドウの枠は有効面の外にはみ出る
// 大きさは有効面より左右2辺、上下2辺の枠の分だけ大きくなる。
// ウインドウの位置も左1辺、上1辺分だけ左上にずれる事になる。
WkMonitorInfo := Self.MonitorInfo;
if FTargetForm.BorderStyle <> bsNone then
begin
// ウインドウの枠の幅を取得
WkSmCxframe := GetSystemMetrics(SM_CXFRAME);
WkSmCyframe := GetSystemMetrics(SM_CYFRAME);
end
else begin
// BorderStyleがbsNoneの場合は枠がないため枠の大きさを考慮しない
WkSmCxframe := 0;
WkSmCyframe := 0;
end;
WkPositionX := WkMonitorInfo.rcWork.Left - WkSmCxframe;
WkPositionY := WkMonitorInfo.rcWork.Top - WkSmCyframe;
WkSizeX :=
WkMonitorInfo.rcWork.Right - WkMonitorInfo.rcWork.Left +
(WkSmCxframe * 2);
WkSizeY :=
WkMonitorInfo.rcWork.Bottom - WkMonitorInfo.rcWork.Top +
(WkSmCyframe * 2);
// 隣のモニタに枠の部分がはみでて見えてしまうのを防止するために
// ちらつきを最小限にして元のサイズに戻す→最大化をする
// ウィンドウの配置情報を保持
ZeroMemory(@WkSaveWindowPlacement, sizeof(WkSaveWindowPlacement));
WkSaveWindowPlacement.length := sizeof(WkSaveWindowPlacement);
GetWindowPlacement(FTargetForm.Handle, @WkSaveWindowPlacement);
WkWindowPlacement.length := SizeOf(WkWindowPlacement);
WkWindowPlacement.showCmd := SW_SHOWNORMAL;
WkWindowPlacement.flags := WPF_RESTORETOMAXIMIZED;
WkWindowPlacement.rcNormalPosition :=
Rect(WkPositionX, WkPositionY,
WkPositionX + WkSizeX, WkPositionY + WkSizeY);
SetWindowPlacement(FTargetForm.Handle, @WkWindowPlacement);
// 再最大化する
ShowWindow(FTargetForm.Handle, SW_SHOWMAXIMIZED);
// 保持していたウィンドウの配置情報を戻す
SetWindowPlacement(FTargetForm.Handle, @WkSaveWindowPlacement);
end;
end;
end;
procedure TMultiMonitorMaxInfoCalc.WMLeftGetMinMaxInfo(
var msg: TWMGetMinMaxInfo);
var
WkMonitorInfo : TMonitorInfo;
WkSizeX, WkSizeY: Integer;
WkPositionX, WkPositionY: Integer;
begin
// マルチモニタ対応の有効面の取得
WkMonitorInfo := Self.MonitorInfo;
WkPositionX := WkMonitorInfo.rcWork.Left;
WkPositionY := WkMonitorInfo.rcWork.Top;
WkSizeX :=
Min(FMaxSizeX,
WkMonitorInfo.rcWork.Right - WkMonitorInfo.rcWork.Left);
WkSizeY :=
WkMonitorInfo.rcWork.Bottom - WkMonitorInfo.rcWork.Top;
msg.MinMaxInfo.ptMaxSize.X := WkSizeX;
msg.MinMaxInfo.ptMaxSize.Y := WkSizeY;
// GetMonitorInfo,Self.Top,Self.Leftで帰ってくる値は
// プライマリモニタの左上を原点(0,0)とした値が帰ってくるが、
// msg.MinMaxInfo.ptMaxPositionは
// BorderStyleがbsSizeableおよびbsSingleの場合のみ
// 各種モニタごとの有効面の左上を原点(0,0)
// とした座標を指定しなければいけない
// それ以外のBorderStyleでは
// 各種モニタごとの左上を原点(0,0)とした座標を指定する。
if (TargetForm.BorderStyle = bsSizeable) or
(TargetForm.BorderStyle = bsSingle) or
(FSetWMGetMinMaxInfo2 = True) then
begin
WkPositionX := WkPositionX - WkMonitorInfo.rcWork.Left;
WkPositionY := WkPositionY - WkMonitorInfo.rcWork.Top;
FSetWMGetMinMaxInfo2 := False;
end
else begin
WkPositionX := WkPositionX - WkMonitorInfo.rcMonitor.Left;
WkPositionY := WkPositionY - WkMonitorInfo.rcMonitor.Top;
end;
msg.MinMaxInfo.ptMaxPosition.X :=
WkPositionX;
msg.MinMaxInfo.ptMaxPosition.Y :=
WkPositionY;
end;
procedure TMultiMonitorMaxInfoCalc.WMClientGetMinMaxInfo(
var msg: TWMGetMinMaxInfo);
var
WkMonitorInfo : TMonitorInfo;
WkSizeX, WkSizeY: Integer;
WkPositionX, WkPositionY: Integer;
begin
// マルチモニタ対応の有効面の取得
WkMonitorInfo := Self.MonitorInfo;
// BorderStyleがそれ以外の場合は
// 最大化した際に有効面を無視してしまう仕様のため、
// 自前で有効面の制限を設定する。
WkSizeX :=
WkMonitorInfo.rcWork.Right - WkMonitorInfo.rcWork.Left;
WkSizeY :=
WkMonitorInfo.rcWork.Bottom - WkMonitorInfo.rcWork.Top;
WkPositionX := WkMonitorInfo.rcWork.Left;
WkPositionY := WkMonitorInfo.rcWork.Top;
msg.MinMaxInfo.ptMaxSize.X := WkSizeX;
msg.MinMaxInfo.ptMaxSize.Y := WkSizeY;
// GetMonitorInfo,Self.Top,Self.Leftで帰ってくる値は
// プライマリモニタの左上を原点(0,0)とした値が帰ってくるが、
// msg.MinMaxInfo.ptMaxPositionは
// BorderStyleがbsSizeableおよびbsSingleの場合のみ
// 各種モニタごとの有効面の左上を原点(0,0)
// とした座標を指定しなければいけない
// それ以外のBorderStyleでは
// 各種モニタごとの左上を原点(0,0)とした座標を指定する。
if (TargetForm.BorderStyle = bsSizeable) or
(TargetForm.BorderStyle = bsSingle) or
(FSetWMGetMinMaxInfo2 = True) then
begin
WkPositionX := WkPositionX - WkMonitorInfo.rcWork.Left;
WkPositionY := WkPositionY - WkMonitorInfo.rcWork.Top;
FSetWMGetMinMaxInfo2 := False;
end
else begin
WkPositionX := WkPositionX - WkMonitorInfo.rcMonitor.Left;
WkPositionY := WkPositionY - WkMonitorInfo.rcMonitor.Top;
end;
msg.MinMaxInfo.ptMaxPosition.X :=
WkPositionX;
msg.MinMaxInfo.ptMaxPosition.Y :=
WkPositionY;
end;
procedure TMultiMonitorMaxInfoCalc.WMRightGetMinMaxInfo(
var msg: TWMGetMinMaxInfo);
var
WkMonitorInfo : TMonitorInfo;
WkSizeX, WkSizeY: Integer;
WkPositionX, WkPositionY: Integer;
begin
// マルチモニタ対応の有効面の取得
WkMonitorInfo := Self.MonitorInfo;
WkSizeX :=
Min(FMaxSizeX,
WkMonitorInfo.rcWork.Right - WkMonitorInfo.rcWork.Left);
WkSizeY :=
WkMonitorInfo.rcWork.Bottom - WkMonitorInfo.rcWork.Top;
WkPositionX := WkMonitorInfo.rcWork.Right - WkSizeX;
WkPositionY := WkMonitorInfo.rcWork.Top;
msg.MinMaxInfo.ptMaxSize.X := WkSizeX;
msg.MinMaxInfo.ptMaxSize.Y := WkSizeY;
// GetMonitorInfo,Self.Top,Self.Leftで帰ってくる値は
// プライマリモニタの左上を原点(0,0)とした値が帰ってくるが、
// msg.MinMaxInfo.ptMaxPositionは
// BorderStyleがbsSizeableおよびbsSingleの場合のみ
// 各種モニタごとの有効面の左上を原点(0,0)
// とした座標を指定しなければいけない
// それ以外のBorderStyleでは
// 各種モニタごとの左上を原点(0,0)とした座標を指定する。
if (TargetForm.BorderStyle = bsSizeable) or
(TargetForm.BorderStyle = bsSingle) or
(FSetWMGetMinMaxInfo2 = True) then
begin
WkPositionX := WkPositionX - WkMonitorInfo.rcWork.Left;
WkPositionY := WkPositionY - WkMonitorInfo.rcWork.Top;
FSetWMGetMinMaxInfo2 := False;
end
else begin
WkPositionX := WkPositionX - WkMonitorInfo.rcMonitor.Left;
WkPositionY := WkPositionY - WkMonitorInfo.rcMonitor.Top;
end;
msg.MinMaxInfo.ptMaxPosition.X :=
WkPositionX;
msg.MinMaxInfo.ptMaxPosition.Y :=
WkPositionY;
end;
procedure TMultiMonitorMaxInfoCalc.WMTopGetMinMaxInfo(
var msg: TWMGetMinMaxInfo);
var
WkMonitorInfo : TMonitorInfo;
WkSizeX, WkSizeY: Integer;
WkPositionX, WkPositionY: Integer;
begin
// マルチモニタ対応の有効面の取得
WkMonitorInfo := Self.MonitorInfo;
WkPositionX := WkMonitorInfo.rcWork.Left;
WkPositionY := WkMonitorInfo.rcWork.Top;
WkSizeX :=
WkMonitorInfo.rcWork.Right - WkMonitorInfo.rcWork.Left;
WkSizeY :=
Min(FMaxSizeY,
WkMonitorInfo.rcWork.Bottom - WkMonitorInfo.rcWork.Top);
msg.MinMaxInfo.ptMaxSize.X := WkSizeX;
msg.MinMaxInfo.ptMaxSize.Y := WkSizeY;
// GetMonitorInfo,Self.Top,Self.Leftで帰ってくる値は
// プライマリモニタの左上を原点(0,0)とした値が帰ってくるが、
// msg.MinMaxInfo.ptMaxPositionは
// BorderStyleがbsSizeableおよびbsSingleの場合のみ
// 各種モニタごとの有効面の左上を原点(0,0)
// とした座標を指定しなければいけない
// それ以外のBorderStyleでは
// 各種モニタごとの左上を原点(0,0)とした座標を指定する。
if (TargetForm.BorderStyle = bsSizeable) or
(TargetForm.BorderStyle = bsSingle) or
(FSetWMGetMinMaxInfo2 = True) then
begin
WkPositionX := WkPositionX - WkMonitorInfo.rcWork.Left;
WkPositionY := WkPositionY - WkMonitorInfo.rcWork.Top;
FSetWMGetMinMaxInfo2 := False;
end
else begin
WkPositionX := WkPositionX - WkMonitorInfo.rcMonitor.Left;
WkPositionY := WkPositionY - WkMonitorInfo.rcMonitor.Top;
end;
msg.MinMaxInfo.ptMaxPosition.X :=
WkPositionX;
msg.MinMaxInfo.ptMaxPosition.Y :=
WkPositionY;
end;
{ TMonitorInfoParam }
function TMonitorInfoParam.DisplayMonitorInfo: TMonitorInfo;
begin
GetMonitorInfoParam;
result := FDisplayMonitorInfo;
end;
procedure TMonitorInfoParam.GetMonitorInfoParam;
begin
// マルチモニタ対応のモニタ情報の取得(プライマリモニタの左上とした座標)
ZeroMemory(@FMonitorInfo, sizeof(FMonitorInfo));
FMonitorInfo.cbSize := SizeOf(FMonitorInfo);
if GetMonitorInfo(FTargetForm.Monitor.Handle, @FMonitorInfo) = False then
begin
// 念のためリトライ(いるのかな・・・?)
Sleep(100);
if GetMonitorInfo(FTargetForm.Monitor.Handle, @FMonitorInfo) = False then
begin
raise Exception.Create('APIのGetMonitorInfo関数でエラーが発生しました' + #13#10 +
'プログラミングエラーです、開発者にお問い合わせください');
end;
end;
// 現在のモニタの左上を原点とした座標の取得
FDisplayMonitorInfo.cbSize := SizeOf(FDisplayMonitorInfo);
FDisplayMonitorInfo.dwFlags := FMonitorInfo.dwFlags;
FDisplayMonitorInfo.rcMonitor.Top := 0;
FDisplayMonitorInfo.rcMonitor.Left := 0;
FDisplayMonitorInfo.rcMonitor.Bottom :=
FMonitorInfo.rcMonitor.Bottom - FMonitorInfo.rcMonitor.Top;
FDisplayMonitorInfo.rcMonitor.Right :=
FMonitorInfo.rcMonitor.Right - FMonitorInfo.rcMonitor.Left;
FDisplayMonitorInfo.rcWork.Top :=
FMonitorInfo.rcWork.Top - FMonitorInfo.rcMonitor.Top;
FDisplayMonitorInfo.rcWork.Left :=
FMonitorInfo.rcWork.Left - FMonitorInfo.rcMonitor.Left;
FDisplayMonitorInfo.rcWork.Bottom :=
FMonitorInfo.rcWork.Bottom - FMonitorInfo.rcMonitor.Top;
FDisplayMonitorInfo.rcWork.Right :=
FMonitorInfo.rcWork.Right - FMonitorInfo.rcMonitor.Left;
end;
function TMonitorInfoParam.MonitorCount: Integer;
begin
result := Screen.MonitorCount;
end;
function TMonitorInfoParam.MonitorInfo: TMonitorInfo;
begin
GetMonitorInfoParam;
result := FMonitorInfo;
end;
function TMonitorInfoParam.MonitorNum: Integer;
begin
result := FTargetForm.Monitor.MonitorNum;
end;
procedure TMonitorInfoParam.SetTargetForm(const Value: TForm);
begin
FTargetForm := Value;
end;
end.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, TypInfo, MultiMon, Math, MultiMonitorParam;
type
TForm1 = class(TForm)
Button1: TButton;
Panel1: TPanel;
MaxAlignRadioGroup: TRadioGroup;
Button2: TButton;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure Panel1Resize(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure MaxAlignRadioGroupClick(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private 宣言 }
FMultiMonitorMaxInfoCalc: TMultiMonitorMaxInfoCalc;
// 最大化時にWM_GETMINMAXINFOでフォームの大きさを調整した際に
// フォームが移動できてしまうのを防止するためのコード
procedure WMSysCommand(var Message: TWMSysCommand); message WM_SYSCOMMAND;
// フォームの大きさと位置の制限
procedure WMGetMinMaxInfo(var msg : TWMGetMinMaxInfo); message WM_GETMINMAXINFO;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
{ TForm1 }
procedure TForm1.WMSysCommand(var Message: TWMSysCommand);
begin
if IsZoomed(Self.Handle) and ((Message.CmdType and $FFF0) = SC_MOVE) then
begin
Exit;
end;
inherited;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
WkMonitorInfo: TMonitorInfo;
WkDisplayMonitorInfo: TMonitorInfo;
begin
//マルチモニタ対応のGetMonitorInfoを使う
WkMonitorInfo := FMultiMonitorMaxInfoCalc.MonitorInfo;
WkDisplayMonitorInfo := FMultiMonitorMaxInfoCalc.DisplayMonitorInfo;
ShowMessage(
'●モニタの情報' + #13#10 +
'モニタの数(1~?):' +
IntToStr(FMultiMonitorMaxInfoCalc.MonitorCount) + #13#10 + 'フォームが表示されているモニタ番号(0~?):' + IntToStr(Self.Monitor.MonitorNum) + #13#10#13#10 +
'●モニタの位置とサイズを表す(プライマリモニタ左上原点)' + #13#10 +
'Left :' + IntToStr(WkMonitorInfo.rcMonitor.Left) + #13#10 +
'Top :' + IntToStr(WkMonitorInfo.rcMonitor.Top) + #13#10 +
'Right :' + IntToStr(WkMonitorInfo.rcMonitor.Right) + #13#10 +
'Bottom:' + IntToStr(WkMonitorInfo.rcMonitor.Bottom) + #13#10#13#10 +
'●有効面のモニタの位置とサイズを表す(プライマリモニタ左上原点)' + #13#10 +
'Left :' + IntToStr(WkMonitorInfo.rcWork.Left) + #13#10 +
'Top :' + IntToStr(WkMonitorInfo.rcWork.Top) + #13#10 +
'Right :' + IntToStr(WkMonitorInfo.rcWork.Right) + #13#10 +
'Bottom:' + IntToStr(WkMonitorInfo.rcWork.Bottom) + #13#10#13#10 +
'●モニタの位置とサイズを表す(各種モニタ左上原点)' + #13#10 +
'Left :' + IntToStr(WkDisplayMonitorInfo.rcMonitor.Left) + #13#10 +
'Top :' + IntToStr(WkDisplayMonitorInfo.rcMonitor.Top) + #13#10 +
'Right :' + IntToStr(WkDisplayMonitorInfo.rcMonitor.Right) + #13#10 +
'Bottom:' + IntToStr(WkDisplayMonitorInfo.rcMonitor.Bottom) + #13#10#13#10 +
'●有効面のモニタの位置とサイズを表す(各種モニタ左上原点)' + #13#10 +
'Left :' + IntToStr(WkDisplayMonitorInfo.rcWork.Left) + #13#10 +
'Top :' + IntToStr(WkDisplayMonitorInfo.rcWork.Top) + #13#10 +
'Right :' + IntToStr(WkDisplayMonitorInfo.rcWork.Right) + #13#10 +
'Bottom:' + IntToStr(WkDisplayMonitorInfo.rcWork.Bottom));
end;
procedure TForm1.WMGetMinMaxInfo(var msg: TWMGetMinMaxInfo);
begin
// OnCreateする前にイベントが来てしまう処置
if FMultiMonitorMaxInfoCalc <> nil then
begin
FMultiMonitorMaxInfoCalc.WMGetMinMaxInfo(msg);
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
FMultiMonitorMaxInfoCalc := TMultiMonitorMaxInfoCalc.Create;
FMultiMonitorMaxInfoCalc.TargetForm := Self;
FMultiMonitorMaxInfoCalc.MaxSizeX := 800;
FMultiMonitorMaxInfoCalc.MaxSizeY := 800;
FMultiMonitorMaxInfoCalc.MaxPositionX := 10;
FMultiMonitorMaxInfoCalc.MaxPositionY := 10;
MaxAlignRadioGroup.ItemIndex := Ord(FMultiMonitorMaxInfoCalc.MaxAlign);
end;
procedure TForm1.Panel1Resize(Sender: TObject);
begin
Self.Caption := Self.Caption + 'r';
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
FreeAndNil(FMultiMonitorMaxInfoCalc);
end;
procedure TForm1.MaxAlignRadioGroupClick(Sender: TObject);
begin
FMultiMonitorMaxInfoCalc.MaxAlign := TAlign(MaxAlignRadioGroup.ItemIndex);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
ShowMessage(
'●フォームの情報' + #13#10 +
'Top:' + IntToStr(Self.Top) + #13#10 +
'Left:' + IntToStr(Self.Left) + #13#10 +
'Width:' + IntToStr(Self.Width) + #13#10 +
'Height:' + IntToStr(Self.Height) + #13#10 +
'WindowState:' + GetEnumName(TypeInfo(TWindowState), Ord(Self.WindowState)));
end;
procedure TForm1.Button3Click(Sender: TObject);
begin
FMultiMonitorMaxInfoCalc.SetFormSafePosition;
end;
end.