下記のサイトのコードを一部変更したものです。
Delphi User's Forum
"コモンダイアログの表示位置を変更"
"RE:コモンダイアログの表示位置を変更"
変更箇所
1) 64bit Windows対策(Windows API 引数のキャスト LongInt を Int64 に変更)。
2) ダイアログがスクリーンをはみ出さないように制限。
3) Application.MainFormOnTaskbar := False; モードへの対策
pWinPos の設定を2回実行するようにした(FOpenDialog1MoveFlag と FSaveDialog1MoveFlag を Byteに変更してカウンタに使用)。
追記)3回実行しないとダメな場合があるようです。
メインフォームの名称 FormMain を適切にリネームしてご利用ください。
注1)下記のイベント設定が必要です。
OpenDialog1のイベント
onClose: OpenDialog1Close
onShow: OpenDialog1Show
SaveDialog1のイベント
onClose: SaveDialog1Close
onShow: SaveDialog1Show
注2)InitialDirを有効にするには、呼び出し前にFilenameを空にする必要があります。
Opendialog1.Filename := '';
Opendialog1.InitialDir := EditFolder.Text;
if Opendialog1.Execute then
--- ソースコード ---
uses
, MultiMon;
type
TFormMain = class(TForm)
procedure OpenDialog1Show(Sender: TObject);
procedure OpenDialog1Close(Sender: TObject);
procedure SaveDialog1Show(Sender: TObject);
procedure SaveDialog1Close(Sender: TObject);
private
{ Private 宣言 }
FOpenDialog1hWnd: HWND;
FNewOpenDialog1WndProc, FOldOpenDialog1WndProc: TFarProc;
FOpenDialog1MoveFlag: Byte;
FSaveDialog1hWnd: HWND;
FNewSaveDialog1WndProc, FOldSaveDialog1WndProc: TFarProc;
FSaveDialog1MoveFlag: Byte;
procedure OpenDialog1WndProc(var Message: TMessage);
procedure SaveDialog1WndProc(var Message: TMessage);
public
{ Public 宣言 }
end;
var
FormMain: TFormMain;
implementation
{$R *.dfm}
// フォームTargetを表示しているモニタの座標とサイズを返す(Uses に MultiMon が必要)
{
TMonitorInfoEx = Record
cbSize : Cardinal; // レコードのサイズ
rcMonitor : TRect; // モニタの矩形
rcWork : TRect; // ワークエリアの矩形
dwFlags : Cardinal; // プライマリモニタなら<>0
szDevice : array[0..31] of Char; // デバイス名(NULL終端)
end;
}
Function GetMonitorRect(Target : TForm = Nil) : TRect;
Var LMonitorInfoEx : TMonitorInfoEx;
begin
if Target=Nil then Target := Application.MainForm;
FillChar(LMonitorInfoEx, SizeOf(TMonitorInfoEx), #0);
LMonitorInfoEx.cbSize := SizeOf(LMonitorInfoEx);
GetMonitorInfo(Target.Monitor.Handle, @LMonitorInfoEx);
Result := LMonitorInfoEx.rcWork; // 有効エリアを返す
end;
//------ for FormMain.OpenDialog1 ------
// OpenDialog1 OnShowイベント
procedure TFormMain.OpenDialog1Show(Sender: TObject);
begin
if ofOldStyleDialog in (Sender as TOpenDialog).Options then
FOpenDialog1hWnd := (Sender as TOpenDialog).Handle
else
FOpenDialog1hWnd := GetParent((Sender as TOpenDialog).Handle);
// 動的サブクラス化
FOpenDialog1MoveFlag := 2; // 位置調整していない
FNewOpenDialog1WndProc := MakeObjectInstance(OpenDialog1WndProc);
FOldOpenDialog1WndProc := Pointer(GetWindowLong(FOpenDialog1hWnd, GWL_WNDPROC));
SetWindowLong(FOpenDialog1hWnd, GWL_WNDPROC, Int64(FNewOpenDialog1WndProc));
end;
// OpenDialog1 OnCloseイベント
procedure TFormMain.OpenDialog1Close(Sender: TObject);
begin
// 動的サブクラス化を解除する
SetWindowLong(FOpenDialog1hWnd, GWL_WNDPROC, INT64(FOldOpenDialog1WndProc));
FreeObjectInstance(FNewOpenDialog1WndProc);
FOpenDialog1hWnd := HWND(nil);
FNewOpenDialog1WndProc := nil;
FOldOpenDialog1WndProc := nil;
end;
// OpenDialog1の新しいウィンドウプロシージャ
procedure TFormMain.OpenDialog1WndProc(var Message: TMessage);
var
pWinPos: PWindowPos;
ScreenRect, mc, rc: TRect;
begin
if (Message.Msg = WM_WINDOWPOSCHANGING) and (FOpenDialog1MoveFlag > 0) then begin
Dec(FOpenDialog1MoveFlag);
pWinPos := PWindowPos(Message.LParam);
pWinPos^.flags := pWinPos^.flags and not(SWP_NOMOVE);
// 表示位置をスクリーン座標で設定する
ScreenRect := GetMonitorRect(FormMain);
GetWindowRect(FormMain.Handle, mc);
GetWindowRect(FOpenDialog1hWnd, rc);
pWinPos^.x := mc.left + (mc.Width - rc.Width) div 2;
pWinPos^.y := mc.Top + (mc.Height - rc.Height) div 2;
// スクリーンからはみ出さないように制限する
if pWinPos^.y<ScreenRect.Top then pWinPos^.y := ScreenRect.Top;
if pWinPos^.y>(ScreenRect.Bottom-rc.Height) then pWinPos^.y := ScreenRect.Bottom-rc.Height;
if pWinPos^.x<ScreenRect.Left then pWinPos^.x := ScreenRect.Left;
if pWinPos^.x>(ScreenRect.Right-rc.Width) then pWinPos^.x := ScreenRect.Right-rc.Width;
end;
Message.Result := CallWindowProc(FOldOpenDialog1WndProc, FOpenDialog1hWnd, Message.Msg, Message.WParam, Message.LParam);
end;
//------ for FormMain.SaveDialog1 ------
//SaveDialog1 OnShowイベント
procedure TFormMain.SaveDialog1Show(Sender: TObject);
begin
if ofOldStyleDialog in (Sender as TSaveDialog).Options then
FSaveDialog1hWnd := (Sender as TSaveDialog).Handle
else
FSaveDialog1hWnd := GetParent((Sender as TSaveDialog).Handle);
// 動的サブクラス化
FSaveDialog1MoveFlag := 2; // 位置調整していない
FNewSaveDialog1WndProc := MakeObjectInstance(SaveDialog1WndProc);
FOldSaveDialog1WndProc := Pointer(GetWindowLong(FSaveDialog1hWnd, GWL_WNDPROC));
SetWindowLong(FSaveDialog1hWnd, GWL_WNDPROC, Int64(FNewSaveDialog1WndProc));
end;
// SaveDialog1 OnCloseイベント
procedure TFormMain.SaveDialog1Close(Sender: TObject);
begin
//動的サブクラス化を解除する
SetWindowLong(FSaveDialog1hWnd, GWL_WNDPROC, Int64(FOldSaveDialog1WndProc));
FreeObjectInstance(FNewSaveDialog1WndProc);
FSaveDialog1hWnd := HWND(nil);
FNewSaveDialog1WndProc := nil;
FOldSaveDialog1WndProc := nil;
end;
// SaveDialog1の新しいウィンドウプロシージャ
procedure TFormMain.SaveDialog1WndProc(var Message: TMessage);
var
pWinPos: PWindowPos;
ScreenRect, mc, rc: TRect;
begin
if (Message.Msg = WM_WINDOWPOSCHANGING) and (FSaveDialog1MoveFlag > 0) then begin
Dec(FSaveDialog1MoveFlag);
pWinPos := PWindowPos(Message.LParam);
pWinPos^.flags := pWinPos^.flags and not(SWP_NOMOVE);
// 表示位置をスクリーン座標で設定する
ScreenRect := GetMonitorRect(FormMain);
GetWindowRect(FormMain.Handle, mc);
GetWindowRect(FSaveDialog1hWnd, rc);
pWinPos^.x := mc.left + (mc.Width - rc.Width) div 2;
pWinPos^.y := mc.Top + (mc.Height - rc.Height) div 2;
// スクリーンからはみ出さないように制限する
if pWinPos^.y<ScreenRect.Top then pWinPos^.y := ScreenRect.Top;
if pWinPos^.y>(ScreenRect.Bottom-rc.Height) then pWinPos^.y := ScreenRect.Bottom-rc.Height;
if pWinPos^.x<ScreenRect.Left then pWinPos^.x := ScreenRect.Left;
if pWinPos^.x>(ScreenRect.Right-rc.Width) then pWinPos^.x := ScreenRect.Right-rc.Width;
end;
Message.Result := CallWindowProc(FOldSaveDialog1WndProc, FSaveDialog1hWnd, Message.Msg, Message.WParam, Message.LParam);
end;
//------ end ------
参考)実行時に基準にするフォームを変更したい場合。
1. 変数 Form_Main を追加する。
Var
Form_Main : TForm = Nil;
2. GetMonitorRect(FormMain) と GetWindowRect(FormMain.Handle, mc) の引数 FormMain を Form_Main に変更する。
ScreenRect := GetMonitorRect(Form_Main);
GetWindowRect(Form_Main.Handle, mc);
3. 基準にするフォームを指定してから呼び出す
Form_Main := Form2;
if OpenDialog1.Execute then // Form2の中央に表示する
...
Form_Main := Form3;
if OpenDialog1.Execute then // Form3の中央に表示する
...
if OpenDialog1.Execute then // Form3の中央に表示する
4. 念のため GetMonitorRect 関数の先頭行を下記に変更すると安心かも。
if Target=Nil then begin Target := Application.MainForm; Form_Main := Target; end;
|