Delphi Programming / Object Pascal
[掲載 2014年06月22日] [更新 2015年12月11日] Delphi 一般・その他
ダイアログ関係の関数類
動作確認等
Windows 7 U64(SP1) + Delphi XE(UP1) Pro
2015年12月11日
システム関係のアイコンをリソースから取得する記事を追加
主に Dialogs ユニットに実装されているダイアログ関数の紹介記事です.これらの関数類は,主にメッセージ表示用として使用されています.
Delphi のオンラインヘルプの,これらの関数の記事は,実際の動作とは違う記述があります.各自で確認してください.
ダイアログのテンプレートも用意されています.IDE の [ファイル] [新規作成] から選択して使用できます.これらは,ユニットを直接使用することも,継承して利用することもできます.
Windows のシステム関係ダイアログを表示するための専用関数はありません.
ShellExecute, ShellExecuteEx あるいは CreateProcess 関数等を使用して起動,表示します.
ダイアログを表示する際,位置とサイズを指定して表示するサンプルと,ダイアログのカスタマイズの例を掲載していますが,全てのダイアログに対する汎用的な方法はありません.また必ずしも実用性があるわけではありません.
コミュニティの場で見かけた質問内容を,実際に確認するためのテストもあります.
[備考]
カスタマイズしたダイアログを表示する確実な方法は TForm から自作することです.そうすれば表示位置や表示サイズも,フォントの大きさも自由に設計できます.
ただし,画像は自分で用意する必要があります.
図1
ダイアログのテンプレート
本ページで掲載しているダイアログ以外にも,以下のダイアログがあります.
これらは,主にデータベースのログイン (ログオン) の際に使用するダイアログです.これらのダイアログは,TForm あるいは TForm を継承したフォームで作成されています.戻り値は論理値です.
ログインダイアログは,デフォルトでは英語表記になるようです.ライブラリパスに以下を追加すると日本語表記になります.
$ (BDS)\source\db\ja // Delphi XE
$ (BDS)\source\data\vclctrls\ja // Delphi XE5
図2
DBLogDlg.LoginDialog
図3
DBLogDlg.LoginDialogEx
User Name 変更不可の DBLogDlg.LoginDialog
図4
DBLogDlg.RemoteLoginDialog
図5
DBPWDlg.PasswordDialog
コンポーネントパレットにダイアログ表示用のコンポーネント類があります.これらは主に,ファイルを選択したり,保存したり,各種設定のためのダイアログです.
Delphi 2007 で,Windows Vista 対応になりました.それに伴って,Vista 専用のダイアログが実装されています.それらは [Vista Dialogs] タブにあります.
[Vista Dialogs] の中に TTaskDialog というのがあります.このダイアログは多機能です.
Delphi 2007 より前の Delphi でも,Windows API の TaskDialogIndirect 関数を使用すると,このダイアログと同じ機能のダイアログを作成することができます.
図6
ダイアログ関係コンポーネント
上は [Dialogs] タブ
下は [Vista Dialogs] タブで,以下のコンポーネントがある
TFileOpenDialog
TFileSaveDialog
TTaskDialog
TTaskDialog は,Flags プロパティに tfPositionRelativeToWindow メンバを含めると,親ウィンドウの中央に表示されます.また,以下のように,OnDialogCreated
イベント内で SetWindowsPos 関数等を使用して,ダイアログの表示位置を設定することができます.
copy code
procedure TForm1.TaskDialog1DialogCreated(Sender: TObject);
var
LPosX : Integer;
LPosY : Integer;
begin
LPosX := 100 ;
LPosY := 100 ;
SetWindowPos(TTaskDialog(Sender).Handle,
0 ,
LPosX,
LPosY,
0 ,
0 ,
SWP_NOSIZE or SWP_NOZORDER);
end ;
[備考]
TOpenDialog コンポーネント等,エクスプローラ風のダイアログでは,ランタイムテーマが無効の時は,ダイアログのスタイルは Windows XP で表示した時と同じになります.ランタイムテーマが有効な場合,OnShow イベントの実現部を実装すると,ダイアログのススタイルは Windows XP で表示した時と同じになります.また,ランタイムテーマが有効な場合,継承クラスを作成して DoShow メソッドを実装しても,その DoShow メソッドは実行されません.
[Vista Dialogs] タブの TFileOpenDialog コンポーネント等には,OnShow イベントも DoShow メソッドもありません.
02_ UseLatestCommonDialogs 変数
Windows Vista, 7 では,ダイアログのデザインが変更されています.
下の図は MessageDlg 関数でダイアログを表示したものです.最初の図が Windows Vista 以降でのダイアログです.UseLatestCommonDialogs 変数の値を False にすると,その下の図のように,ダイアログのデザインが Windows XP スタイルとなります.
UseLatestCommonDialogs 変数の値は,デフォルトで True となっています.Windows XP で実行する場合は,この変数の値は無視され,False と同じ結果となります.
図7
UseLatestCommonDialogs が True の時
図8
UseLatestCommonDialogs が False の時
copy code
procedure TForm1.Button1Click(Sender: TObject);
var
StrText : String ;
begin
Dialogs.UseLatestCommonDialogs := True;
StrText := 'ダイアログ関係関数テスト' ;
Dialogs.MessageDlg(StrText, mtInformation, [mbYes], 0 );
end ;
procedure TForm1.Button2Click(Sender: TObject);
var
StrText : String ;
begin
Dialogs.UseLatestCommonDialogs := False;
StrText := 'ダイアログ関係関数テスト' ;
Dialogs.MessageDlg(StrText, mtInformation, [mbYes], 0 );
end ;
03_ Dialogs.ShowMessage - [ OK ] ボタンだけの簡単なダイアログ
[OK] ボタンだけの単純なダイアログです.
ダイアログのキャプションはアプリケーションのタイトルとなります.関数形式ではありません.したがって戻り値はありません.
図9
[OK] ボタンだけのダイアログ
copy code
procedure TForm1.Button1Click(Sender: TObject);
var
StrText : String ;
begin
StrText := 'ダイアログ関係関数テスト' ;
Dialogs.ShowMessage(StrText);
end ;
04_ Dialogs.ShowMessageFmt - 書式設定機能付きダイアログ
書式設定の機能付きの ShowMessage です.
図10
書式設定機能付き ShowMessage
copy code
procedure TForm1.Button1Click(Sender: TObject);
var
hWindow : HWND;
begin
hWindow := Handle;
Application.Title := 'ダイアログ関係関数テスト' ;
Dialogs.ShowMessageFmt('%s $%.8x ( %.d )' , ['16 進数表示' , hWindow, hWindow]);
end ;
05_ Dialogs.ShowMessagePos - 位置指定ダイアログ
表示位置が指定可能な ShowMessage です.
図11
位置を指定して表示した例
copy code
procedure TForm1.Button1Click(Sender: TObject);
var
StrText : String ;
hWindow : HWND;
begin
StrText := 'ダイアログ関係関数テスト' ;
Dialogs.ShowMessagePos(StrText, Self.Left + 20 , Self.Top + 100 );
end ;
06_ Application.ShowException 関数 - 例外処理内のダイアログ
例外処理の中で使用するダイアログ関数です.
例外処理の中で意識的に使用しない場合は,このダイアログが自動的に表示されます.
図12
例外処理の中で使用するダイアログ
copy code
procedure TForm1.Button1Click(Sender: TObject);
var
A : Integer;
B : Integer;
begin
A := 0 ;
try
B := 123 div A;
except
on E: EZeroDivide do
Application.ShowException(E);
end ;
end ;
例外処理のクラスと例外メッセージの文字列は,例えば次のようなコードで取得できます.
例外やエラーが発生した場合,どんな例外が発生したのかを調べる目的に使用できます.
図13
例外処理クラスと例外メッセージ文字列の取得
copy code
procedure TForm1.Button2Click(Sender: TObject);
var
A : Integer;
B : Integer;
StrText : String ;
begin
A := 0 ;
try
B := 123 div A;
except
on E: Exception do begin
StrText := E.ClassName + sLineBreak + E.Message;
Application.MessageBox(PChar(StrText), '情報' , MB_ICONINFORMATION);
end ;
end ;
end ;
07_ Application.MessageBox 関数
TApplication コンポーネントの MessageBox 関数の実行例です.
この関数は UseLatestCommonDialogs 変数の値の影響を受けません.表示するボタンも複数表示できますが,MessageDlg 関数と違い,[すべて] のボタンを表示する機能はありません.
この関数は,内部で Windows API の MessageBox 関数を実行しています.このように,内部で他の関数を実行している関数を,その関数のラッパ関数といっています.ラッピング,つまり包み込んでいるという意味です.Windows API の MessageBox 関数のラッパともいいます.
下図は,表示するアイコンを変えて実行した結果です.アイコンは,指定しなければ表示しません.
また,MB_TOPMOST をフラグに含めると,Z オーダーの最上位として表示され,他のウインドウがアクティブになっても,最前面に表示されたままとなります.
図14
アイコンの指定が MB_ICONWARNING の場合
図15
アイコンの指定が MB_ICONINFORMATION の場合
図16
アイコンの指定が MB_ICONQUESTION の場合
図17
アイコンの指定が MB_ICONSTOP の場合
copy code
procedure TForm1.Button1Click(Sender: TObject);
var
StrText : String ;
begin
StrText := 'ダイアログ関係関数テスト' ;
Application.MessageBox(PChar(StrText), '情報' , MB_ICONWARNING or MB_TOPMOST);
end ;
procedure TForm1.Button2Click(Sender: TObject);
var
StrText : String ;
begin
StrText := 'ダイアログ関係関数テスト' ;
Application.MessageBox(PChar(StrText), '情報' , MB_ICONINFORMATION);
end ;
procedure TForm1.Button3Click(Sender: TObject);
var
StrText : String ;
begin
StrText := 'ダイアログ関係関数テスト' ;
Application.MessageBox(PChar(StrText), '情報' , MB_ICONQUESTION);
end ;
procedure TForm1.Button4Click(Sender: TObject);
var
StrText : String ;
begin
StrText := 'ダイアログ関係関数テスト' ;
Application.MessageBox(PChar(StrText), '情報' , MB_ICONSTOP,);
end ;
図18
複数のボタンを表示した場合
copy code
procedure TForm1.Button5Click(Sender: TObject);
var
StrText : String ;
iRet : Integer;
begin
StrText := 'ダイアログ関係関数テスト' ;
iRet := Application.MessageBox(PChar(StrText), '情報' ,
MB_YESNOCANCEL or MB_ICONQUESTION,);
if iRet = IDYES then begin
// [はい]を選択した時の処理
end else
if iRet = IDNO then begin
// [いいえ]を選択した時の処理
end else
if iRet = IDCANCEL then begin
// [キャンセル]を選択した時の処理
end ;
end ;
複数のボタンを表示した場合,デフォルトで,どのボタンにフォーカスするかを指定できます.
ダイアログを表示した直後,[Enter] キーを押下すると,このボタンクリックと同じになります.図では [いいえ] をデフォルトのボタンにしています.
図19
デフォルトのフォーカスボタンの指定
copy code
procedure TForm1.Button6Click(Sender: TObject);
var
StrText : String ;
iRet : Integer;
begin
StrText := 'ダイアログ関係関数テスト' ;
iRet := Application.MessageBox(PChar(StrText), '情報' ,
MB_YESNOCANCEL or MB_DEFBUTTON2 or MB_ICONQUESTION);
if iRet = IDYES then begin
// [はい]を選択した時の処理
end else
if iRet = IDNO then begin
// [いいえ]を選択した時の処理
end else
if iRet = IDCANCEL then begin
// [キャンセル]を選択した時の処理
end ;
end ;
Windows Vista, Windows 7 であれば,Application.MessageBox 関数で表示するダイアログのフォントを変更できます.[ウィンドウの色とデザイン] のダイアログで [メッセージボックス] の色とフォントサイズを変更します.
ダイアログのオーナーハンドルを指定すること以外は TApplication コンポーネントの同名の関数と機能は同じです.Application.MessageBox 関数のオーナーウィンドウは,ダイアログを表示する時にアクティブなフォームです.
以下のコードでは,オーナウィンドウのハンドルを表示元のフォームとしています.ダイアログを閉じるまでは,このフォームをアクティブにはできません.
オーナーハンドルを 0 にすると,ダイアログの表示中でもフォームにフォーカスが移動できます.フォームを閉じるとダイアログも閉じます.オーナーハンドルが 0 の場合は,オーナーを持たなウインドウです.ダイアログの表示中に他のアプリケーションのウィンドウ等を操作すると,ダイアログを閉じた時にフォームが最前面に表示される保証はありません.
copy code
procedure TForm1.Button1Click(Sender: TObject);
var
StrText : String ;
begin
StrText := 'ダイアログ関係関数テスト' ;
Windows.MessageBox(Handle, PChar(StrText), '情報' , MB_YESNO or MB_ICONASTERISK);
end ;
09_ Dialogs.MessageDlg 関数 - 自動キャプションダイアログ
表示するアイコンの種類によってキャプションが自動で決まるダイアログです.
表示するアイコンを mtCustom にするとアイコンを表示しません.その場合,キャプションはアプリケーションのタイトルとなります.
このダイアログは [すべて] のボタンを表示できます.ファイルを全て選択するとか,文字列の置換等で全てを実行する際に利用できます.
下図は,表示するアイコンを変えて実行した結果です.
図20
アイコンの指定が mtWarning の場合
図21
アイコンの指定が mtInformation の場合
図22
アイコンの指定が mtConfirmation の場合
実際のアイコンが疑問符 (クエスチョンマーク) にならない理由は,後述の記事を参照
図23
アイコンの指定が mtError の場合
copy code
procedure TForm1.Button1Click(Sender: TObject);
var
StrText : String ;
begin
StrText := 'ダイアログ関係関数テスト' ;
Dialogs.MessageDlg(StrText, mtWarning, [mbYes], 0 );
end ;
procedure TForm1.Button2Click(Sender: TObject);
var
StrText : String ;
begin
StrText := 'ダイアログ関係関数テスト' ;
Dialogs.MessageDlg(StrText, mtInformation, [mbYes], 0 );
end ;
procedure TForm1.Button3Click(Sender: TObject);
var
StrText : String ;
begin
StrText := 'ダイアログ関係関数テスト' ;
Dialogs.MessageDlg(StrText, mtConfirmation, [mbYes], 0 );
end ;
procedure TForm1.Button4Click(Sender: TObject);
var
StrText : String ;
begin
StrText := 'ダイアログ関係関数テスト' ;
Dialogs.MessageDlg(StrText, mtError, [mbYes], 0 );
end ;
MessageDlg 関数も MessageBox 関数と同じく複数のボタンを表示できます.また,デフォルトでフォーカスを与えるボタンを指定できます.
どのボタンを選択したかは,下のコードのように,関数の戻り値で判定します.
図24
表示の時に [キャンセル] ボタンにフォーカスを与えた場合
ただし実用的には問題がある.[Enter] キーで [キャンセル] になってしまう
通常 [Enter] キーは決定の意味
copy code
procedure TForm1.Button5Click(Sender: TObject);
var
StrText : String ;
iRet : Integer;
begin
StrText := 'ダイアログ関係関数テスト' ;
iRet := MessageDlg(StrText, mtConfirmation, [mbYes, mbCancel], 0 , mbCancel);
if iRet = mrCancel then begin
// キャンセルの時の処理
end ;
end ;
10_ Dialogs.MessageDlgPos 関数 - 位置指定
MessageDlg 関数に,表示位置を指定する引数が追加されたダイアログです.
図25
位置を指定して表示した例
表示した時に [すべて] にフォーカスを与えている
copy code
procedure TForm1.Button1Click(Sender: TObject);
var
StrText : String ;
iRet : Integer;
begin
StrText := 'ダイアログ関係関数テスト' ;
iRet := Dialogs.MessageDlgPos(StrText,
mtInformation,
[mbYes, mbAll, mbCancel],
0 ,
Self.Left + 20 ,
Self.Top + 100 ,
mbAll);
if iRet = mrYes then begin
// [はい]を選択した時の処理
end else
if iRet = mrAll then begin
// [全て]を選択した時の処理
end else
if iRet = mrCancel then begin
// [キャンセル]を選択した時の処理
end ;
end ;
11_ Dialogs.MessageDlgPosHelp 関数 - 状況感知ヘルプ表示
MessageDlg 関数に,表示位置を指定する引数と,状況感知型ヘルプ (状況依存型ヘルプ) ファイルを指定する引数を追加したダイアログです.
下のコードでは,ヘルプファイルとして,当サイトのトップページを指定しています.ただし,当サイトの HTML ファイルは,HTML ヘルプとして作成していません.したがって,位置やサイズを変更した後に再表示しても,デフォルトの位置とサイズで表示されることになります.
HTML ヘルプファイルの表示には,uses に HTMLHelpViewer が必要です.
図26
位置を指定してダイアログを表示
図27
ダイアログを表示して [F1] を押下
ヘルプファイルとしてのウェブページを表示
右上のウィンドウがそのヘルプ
copy code
implementation
uses HTMLHelpViewer;
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
StrText : String ;
HelpFile : String ;
begin
HelpFile := 'http:// mrxray.on.coocan.jp/index.htm' ;
StrText := '[F1] 押下で状況感知型ヘルプ表示' ;
Dialogs.MessageDlgPosHelp(StrText,
mtInformation,
[mbYes],
1 ,
Self.Left + 20 ,
Self.Top + 100 ,
HelpFile);
end ;
12_ Dialogs.TaskMessageDlg 関数 - タイトル付きダイアログ
Windows Vista 以降で使用可能なダイアログ関数です.機能的には MessageDlg 関数と同じですが,タイトルを指定する引数が追加されています.
MessageDlg 関数は,以下の条件の時に,この TaskMessageDlg 関数を実行します.
Windows Vista 以降である
ランテーマが有効となっている
UseLatestCommonDialogs 変数の値が True である
MessageDlg 関数は,タイトル文字列の指定がない TaskMessageDlg 関数と同じです.
MessageDlg 関数と同じく,デフォルトでフォーカスを与えるボタンを指定できます.
図28
TaskMessgeDlg ダイアログの表示例
copy code
procedure TForm1.Button1Click(Sender: TObject);
var
StrTitle : String ;
StrText : String ;
begin
if not CheckWin32Version(6 , 0 ) then exit;
StrTitle := 'ダイアログのタイトル' ;
StrText := 'ダイアログ関係関数テスト' ;
Dialogs.TaskMessageDlg(StrTitle, StrText, mtInformation, [mbYes, mball], 0 );
end ;
13_ Dialogs.TaskMessageDlgPos 関数 - 位置指定
TaskMessageDlg 関数に,表示位置を指定する引数が追加されたダイアログです.
図29
位置を指定して表示した例
デフォルトのフォーカスを [すべて] のボタンにしている
copy code
procedure TForm1.Button1Click(Sender: TObject);
var
StrTitle : String ;
StrText : String ;
iRet : Integer;
begin
if not CheckWin32Version(6 , 0 ) then exit;
StrTitle := 'ダイアログのタイトル' ;
StrText := 'ダイアログ関係関数テスト' ;
iRet := Dialogs.TaskMessageDlgPos(StrTitle,
StrText,
mtInformation,
[mbYes, mbAll, mbCancel],
0 ,
Self.Left + 20 ,
Self.Top + 100 ,
mbAll);
if iRet = mrYes then begin
// [はい]を選択した時の処理
end else
if iRet = mrAll then begin
// [全て]を選択した時の処理
end else
if iRet = mrCancel then begin
// [キャンセル]を選択した時の処理
end ;
end ;
14_ Dialogs.TaskMessageDlgPosHelp 関数 - 状況感知ヘルプ表示
TaskMessageDlg 関数に,表示位置を指定する引数と,状況感知型ヘルプ (状況依存型ヘルプ) ファイルを指定する引数を追加したダイアログです.
下のコードでは,ヘルプファイルとして,当サイトのトップページを指定しています.ただし,当サイトの HTML ファイルは,HTML ヘルプとして作成していません.したがって,位置やサイズを変更した後に再表示しても,デフォルトの位置とサイズで表示されることになります.
HTML ヘルプファイルの表示には,uses に HTMLHelpViewer が必要です.
図30
位置を指定してダイアログを表示
図31
ダイアログを表示して [F1] を押下
ヘルプファイルとしてのウェブページを表示
右上のウインドウがそのヘルプ
copy code
implementation
uses HTMLHelpViewer;
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
StrTitle : String ;
StrText : String ;
HelpFile : String ;
begin
if not CheckWin32Version(6 , 0 ) then exit;
StrTitle := 'ダイアログのタイトル' ;
StrText := 'ダイアログ関係関数テスト' ;
HelpFile := 'http:// mrxray.on.coocan.jp/index.htm' ;
Dialogs.TaskMessageDlgPosHelp(StrTitle,
StrText,
mtInformation,
[mbYes],
1 ,
Self.Left + 20 ,
Self.Top + 100 ,
HelpFile);
end ;
15_ Dialogs.InputBox 関数 - 文字列や数値の入力用
数値や文字列の入力用ダイアログです.戻り値は,入力した値で,文字列です.
入力した値が,元の値と同じかを確認する用途に向いています.この関数は InputQuery 関数 のラッパ関数です.内部で InputQuery 関数を実行しています.
InputBox ダイアログと InputQuery ダイアログをカスタマイズする場合,カスタマイズの内容にもよりますが,InputQuery のソースコードを参考にして,自前でダイアログを作成してしまった方がいいかも知れません.
図32
InputBox ダイアログ関数
copy code
procedure TForm1.Button1Click(Sender: TObject);
var
StrCaption : String ;
StrPrompt : String ;
StrDefault : String ;
StrResult : String ;
begin
StrCaption := '入力ダイアログ' ;
StrPrompt := '値の入力' ;
StrDefault := '12345' ;
StrResult := InputBox(StrCaption, StrPrompt, StrDefault);
if StrResult = StrDefault then begin
// [キャンセル]ボタンを選択した時,または入力値に変化がない場合
// または[ESC]でダイアログを閉じた場合
end else begin
// [OK]ボタンを選択した時
end ;
end ;
16_ Dialogs.InputQuery 関数 - 文字列や数値の入力用
数値や文字列の入力用ダイアログです.戻り値は論理値です.
[OK] でダイアログを閉じた時は True,それ以外は False となります.入力した値は,最後の引数に文字列として返されてきます.ダイアログを表示した時の初期値もこの引数で指定します.
InputBox 関数では,初期値と入力した値を独立して扱えますが,この InputQuery 関数では兼用となっています.
このダイアログをカスタマイズする場合,カスタマイズの内容にもよりますが,InputQuery のソースコードを参考にして,自前でダイアログを作成してしまった方がいいかも知れません.
図33
InputQuery ダイアログ関数
copy code
procedure TForm1.Button1Click(Sender: TObject);
var
StrCaption : String ;
StrPrompt : String ;
StrValue : String ;
begin
StrCaption := '入力ダイアログ' ;
StrPrompt := '値の入力' ;
StrValue := '12345' ;
if InputQuery(StrCaption, StrPrompt, StrValue) then begin
// [OK]ボタンを選択した時
end else begin
// [キャンセル]ボタンを選択した時,または[ESC]で閉じた場合
end ;
end ;
17_ Dialogs.CreateMessageDialog 関数 - カスタマイズ自由
ダイアログを表示する関数ではありません.ダイアログを作成する関数です.
戻り値は TForm のクラス型です.したがって,自由にカスタマイズできます.
ダイアログ上 (フォーム上) のコンポーネントは,追加したコンポーネントも含めて,コンポーネントの配列に格納されます.
このダイアログ関数も,デフォルトでフォーカスを与えるボタンを指定できます.
図34
ダイアログの表示例
[ヘルプ] ボタンを表示してみた
ダイアログのタイプ (TMsgDlgType) に mbHelp を含めると,自動的に [ヘルプ] ボタンも表示される
デフォルトのフォーカスを [すべて] のボタンにしている
図35
カスタマイズしたダイアログの例
デフォルトのフォーカスを [ALL] のボタンにしている
copy code
procedure TForm1.Button1Click(Sender: TObject);
var
FrmDlg : TForm;
StrMsg : String ;
iRet : Integer;
begin
StrMsg := 'ダイアログ関係関数テスト' ;
FrmDlg := CreateMessageDialog(StrMsg,
mtInformation,
[mbYes, mbAll, mbCancel],
mbAll);
try
// オーナウィンドウの中央に表示(この場合はTForm1(Form1)
FrmDlg.Position := poOwnerFormCenter;
// [ヘルプ]ボタンを表示してみる
FrmDlg.BorderIcons := FrmDlg.BorderIcons + [biHelp];
iRet := FrmDlg.ShowModal;
if iRet = mrYes then begin
// [はい]でダイアログを閉じた場合
end else
if iRet = mrAll then begin
// [すべて]でダイアログを閉じた場合
end else
if iRet = mrCancel then begin
// [キャンセル]でダイアログを閉じた場合
end ;
finally
FrmDlg.Free;
end ;
end ;
procedure TForm1.Button2Click(Sender: TObject);
var
FrmDlg : TForm;
StrMsg : String ;
iRet : Integer;
begin
StrMsg := 'ダイアログ関数のテスト' ;
FrmDlg := CreateMessageDialog(StrMsg,
mtConfirmation,
[mbYes, mbAll, mbCancel],
mbAll);
try
// 表示前にダイアログ上のコンポーネント類のプロパティを変更
TLabel(FrmDlg.Components[1 ]).Width := FrmDlg.Width;
TLabel(FrmDlg.Components[1 ]).Font.Size := 15 ;
TLabel(FrmDlg.Components[1 ]).Font.Color := clGreen;
TLabel(FrmDlg.Components[1 ]).Font.Style := [fsBold];
TButton(FrmDlg.Components[2 ]).Caption := 'YES' ;
TButton(FrmDlg.Components[3 ]).Caption := 'CANCEL' ;
TButton(FrmDlg.Components[4 ]).Caption := 'ALL' ;
FrmDlg.Height := 200 ;
FrmDlg.Left := Self.Left + 33 ;
FrmDlg.Top := Self.Top + 50 ;
iRet := FrmDlg.ShowModal;
if iRet = mrYes then begin
// [はい]でダイアログを閉じた場合
end else
if iRet = mrAll then begin
// [すべて]でダイアログを閉じた場合
end else
if iRet = mrCancel then begin
// [キャンセル]でダイアログを閉じた場合
end ;
finally
FrmDlg.Free;
end ;
end ;
下図は,CreateMessageDialog 関数で作成したダイアログのコンポーネントを調べたものです.
図36
ダイアログ上のコンポーネントの一覧
コンポーネントの配列として格納されている
コンポーネントのクラスでキャストして操作する
copy code
procedure TForm1.Button1Click(Sender: TObject);
var
FrmDlg : TForm;
StrMsg : String ;
begin
StrMsg := 'ダイアログ関係関数テスト' ;
FrmDlg := CreateMessageDialog(StrMsg,
mtInformation,
[mbYes, mbAll, mbCancel],
mbAll);
try
// イベントを定義してダイアログを表示
FrmDlg.OnShow := OnFrmShow;
FrmDlg.ShowModal;
finally
FrmDlg.Free;
end ;
end ;
procedure TForm1.OnFrmShow(Sender: TObject);
var
i : Integer;
aCompo : TComponent;
begin
Memo1.Lines.Clear;
for i := 0 to TForm(Sender).ComponentCount - 1 do begin
aCompo := TForm(Sender).Components[i];
Memo1.Lines.Add('Components[' + IntToStr(i) +'] : ' + aCompo.ClassName);
end ;
end ;
ダイアログにボタンを追加する例です.
追加したボタンの位置やサイズは自由に変更ですます.元々のボタン類の位置やサイズも,上のサンプルのように,コンポーネント配列の値を使用して,自由に変更できます.
CreateMessageDialog を常用するのであれば,元のソースコードを変更してしまうのも一つの手かもしれません.CreateMessageDialog の継承関係は次のようになっています.
Result が CreateMessageDialog 関数が生成するダイアログです.
TForm --> TMessageForm --> Result := TMessageForm.CreateNew(Application)
図37
ダイアログにボタンを追加
copy code
procedure TForm1.Button1Click(Sender: TObject);
var
FrmDlg : TForm;
StrMsg : String ;
ABtn : TButton;
begin
StrMsg := 'ダイアログ関係関数テスト' ;
FrmDlg := CreateMessageDialog(StrMsg,
mtInformation,
[mbYes, mbAll, mbCancel],
mbAll);
try
// ダイアログにボタンを追加して表示位置を設定
ABtn := TButton.Create(FrmDlg);
ABtn.Parent := FrmDlg;
ABtn.Left := TButton(FrmDlg.Components[4 ]).Left;
ABtn.Top := TButton(FrmDlg.Components[4 ]).top - 30 ;
FrmDlg.ShowModal;
finally
FrmDlg.Free;
end ;
end ;
18_ Dialogs.PromptForFileName 関数 - [ 開く ] [ 保存 ] ダイアログ
[ファイルを開く] または [名前を付けて保存] のダイアログを表示する関数です.
TOpenDialog または TSaveDialog コンポーネントの代わりに使用できますが,該当するダイアログコンポーネントのような詳細な設定はできません.
図38
[ファイルを開く] ダイアログの例
copy code
procedure TForm1.Button1Click(Sender: TObject);
var
AFileName : String ;
AInitialDir : String ;
begin
AFileName := '' ;
AInitialDir := ExpandFileName('../' );
if PromptForFileName(AFileName,
'ユニットファイル (*.pas)|*.pas' ,
'' ,
'' ,
AInitialDir,
False)
then begin
// [開く]または[保存]でダイアログを閉じた場合
end else begin
// [キャンセル]でダイアログを閉じた場合
end ;
end ;
FileCtrl ユニットにフォルダ選択用のダイアログ関数が実装されています.
この関数を使用したサンプルを,下記のリンクに掲載しています.
Delphi 2007 で実装された TFileOpenDialog ダイアログコンポーネントは,フォルダの選択ダイアログとしても使用できます.
Options プロパティに fdoPickFolders フラグがあると,下図のようにフォルダの選択ダイアログになります.ルートフォルダのような指定はないようです.このダイアログは Windows Vista 以降でないと使用できません.
デザインはファイルの選択 (ファイルを開く) ダイアログと同じです.
図39
フォルダの選択
copy code
procedure TForm1.Button1Click(Sender: TObject);
begin
if not CheckWin32Version(6 , 0 ) then exit;
FileOpenDialog1.Options := [fdoPickFolders, fdoPathMustExist, fdoForceFileSystem];
FileOpenDialog1.Execute;
end ;
Windows Vista 以降で,アイコンに mtConfirmation を指定した場合の現象です.
MessageDlg 関数では,実際には感嘆符のアイコンとなります.また,TaskMessageDlg 関数でも疑問符のアイコンではなく,感嘆符のアイコンが表示されます.
TTaskDialog ダイアログコンポーネントの場合,MainIcon プロパティを 32514 にすると,疑問符になります.MainIcon プロパティは,オブジェクト・インスペクタで設定できます.TTaskDialog コンポーネントの機能は,Windows XP 以前では利用できません.
UseLatestCommonDialogs の値を False にすれば疑問符のアイコンが表示されますが,ダイアログのスタイルは Windows XP スタイルとなります.
この現象は,アイコン表示のガイドラインが変更になったことが原因のようです.詳細は,下記のリンクの記事で確認してください.
図40
UseLatestCommonDialogs = True の時
図41
UseLatestCommonDialogs = False の時
指定した通り疑問符が表示される
ただし,Windows XP スタイル
copy code
procedure TForm1.Button1Click(Sender: TObject);
var
StrText : String ;
begin
StrText := 'ダイアログ関係関数テスト' ;
Dialogs.MessageDlg(StrText, mtConfirmation, [mbYes], 0 );
end ;
procedure TForm1.Button2Click(Sender: TObject);
var
StrText : String ;
begin
Dialogs.UseLatestCommonDialogs := False;
StrText := 'ダイアログ関係関数テスト' ;
Dialogs.MessageDlg(StrText, mtConfirmation, [mbYes], 0 );
Dialogs.UseLatestCommonDialogs := True;
end ;
21_ ダイアログの表示位置とサイズの制御 - CreateMessageDialog
CreateMessageDialog 関数で作成したダイアログは TForm 型です.したがって,位置の設定が自由にできます.もちろん,サイズの変更も,フォントも自由に設定できます.
22_ ダイアログの表示位置とサイズの制御 - ローカルフック
ほとんどのダイアログは,その表示位置は画面中央となっています.位置を指定できるダイアログはありますが,表示前にサイズを知ることができません.したがって,フォームの中央に表示するという設定はできません.
Windows のダイアログのクラス名は #32770 (= #$8000) です.
次のコードは,WH_CALLWNDPROC フック関数をローカルフックとして登録します.フックのコールバック関数内で,ダイアログのウィンドウが表示される時の WM_SHOWWINDOW メッセージを捕捉します.そして,クラス名が #32770 のウィンドウの位置を設定します.
フックはローカルフックとします.グローバルフックにしてしまうと,システム全体に対して有効になってしまい,クラス名が #32770 のダイアログ全てが,このコードで設定する位置に表示されてしまうことになります.
TOpenDialog, TSaveDialog 等では,ナビゲーションウィンドウの表示有無,あるいはサイズを変更した直後は,計算通りの位置に表示できない場合があります.
図42
MessageDlg 関数でダイアログを表示
図43
TOpenDialog でダイアログを表示
図44
TFindDialog でダイアログを表示
copy code
implementation
{$R *.dfm}
var
MsgHook: HHOOK;
// -----------------------------------------------------------------------------
// ウィンドウのクラス名を取得する関数
// -----------------------------------------------------------------------------
function GetWindowClassNameStr(hWindow: HWND): String ;
var
Buffer : array [0 ..MAX_PATH-1 ] of Char;
Len : Integer;
begin
FillChar(Buffer, SizeOf(Buffer), #0 );
Len := GetClassName(hWindow, Buffer, Length(Buffer));
if Len > 0 then Result := Buffer else Result := '' ;
end ;
// -----------------------------------------------------------------------------
// WH_CALLWNDPROCフックのコールバック関数
// ダイアログのWM_SHOWWINDOWメッセージを処理
//
// ここではMsg : PMsgではなくMsg: PCWPStructにして,wParamの値を受け取る
// WM_SHOWWINDOWのwParamは1の時表示開始,閉じる時は0
//
// Windowsのダイアログのクラス名は#32770
// -----------------------------------------------------------------------------
function GetMsgProc(Code: Integer; WPar: WPARAM; LPar: LPARAM): LRESULT; stdcall ;
var
pMsgStruct : PCWPStruct;
ClassName : String ;
hWindow : HWND;
DlgRect : TRect;
DlgWidth : Integer;
DlgHeight : Integer;
DlgPosX : Integer;
DlgPosY : Integer;
begin
if (code < 0 ) then begin
Result := CallNextHookEx(MsgHook, Code, WPar, LPar);
exit;
end ;
if (code = HC_ACTION) then begin
pMsgStruct := PCWPStruct(LPar);
if pMsgStruct^.message = WM_SHOWWINDOW then begin
if pMsgStruct^.wParam = 1 then begin
ClassName := GetWindowClassNameStr(pMsgStruct.hwnd);
if ClassName = '#32770' then begin
// ダイアログのハンドルを取得してサイズを取得
hWindow := pMsgStruct^.hwnd;
GetWindowRect(hWindow, DlgRect);
// ダイアログの幅と高さを取得
DlgWidth := DlgRect.Right - DlgRect.Left;
DlgHeight := DlgRect.Bottom - DlgRect.Top;
// Form1の上下左右の中央に表示する
DlgPosX := Form1.Left + (Form1.Width - DlgWidth) div 2 ;
DlgPosY := Form1.Top + (Form1.Height - DlgHeight) div 2 ;
SetWindowPos(hWindow,
0 ,
DlgPosX,
DlgPosY,
0 ,
0 ,
SWP_NOSIZE or SWP_NOZORDER);
end ;
end ;
end ;
end ;
Result := CallNextHookEx(MsgHook, Code, WPar, LPar);
end ;
// =============================================================================
// ローカルフック開始
// =============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
MsgHook := SetWindowsHookEx(WH_CALLWNDPROC, GetMsgProc, 0 , GetCurrentThreadID);
end ;
// =============================================================================
// フック解除
// =============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
UnHookWindowsHookEx(MsgHook);
end ;
// =============================================================================
// ダイアログを表示
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
StrText : String ;
begin
StrText := 'ダイアログの表示位置の制御' ;
Dialogs.MessageDlg(StrText, mtInformation, [mbYes], 0 );
end ;
23_ ダイアログの表示位置とサイズの制御 - PostMessage
表示したダイアログの操作は,次のようなコードでも可能です.
PostMessage でメッセージをメッセージキューにポスト (書き込み) します.PostMessage は処理の結果を待たずにに戻ってきます.その直後に何かしらの操作をすると,ポストされたメッセージの処理が行われることを利用しています.
使用するメッセージは何でも構いません.下のサンプルでは,他の動作にあまり関係がないと思われる WM_APP_XXX を使用しています.
TOpenDialog 等の Windows のコモンダイアログをカプセル化したダイアログは,この方法では制御できません.また,ダイアログ表示前にメッセージキューに対して,何かしらの操作が行われた場合も制御できません.前項のローカルフックの方が,動作は確実です.
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class (TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
protected
procedure WMApp(var Message: TMessage); message WM_APP;
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
{$R *.dfm}
// =============================================================================
// ダイアログを表示
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
PostMessage(Handle, WM_APP, 0 , 0 );
MessageDlg('ダイアログ位置設定' , mtInformation, [mbYes], 0 );
end ;
// =============================================================================
// メッセージ処理
// ダイアログ表示直前にPosMessageでメッセージキューに書き込んでおく
// =============================================================================
procedure TForm1.WMApp(var Message: TMessage);
var
hWindow : HWND;
DlgRect : TRect;
DlgWidth : Integer;
DlgHeight : Integer;
DlgPosX : Integer;
DlgPosY : Integer;
begin
inherited ;
hWindow := FindWindow('#32770' , nil );
// ウィンドウのタイトルが分かっているなら以下の方が確実
hWindow := FindWindow(nil , '情報' );
if hWindow <> 0 then begin
GetWindowRect(hWindow, DlgRect);
// ダイアログの幅と高さを取得
DlgWidth := DlgRect.Right - DlgRect.Left;
DlgHeight := DlgRect.Bottom - DlgRect.Top;
// Form1の上下左右の中央に表示する
DlgPosX := Form1.Left + (Form1.Width - DlgWidth) div 2 ;
DlgPosY := Form1.Top + (Form1.Height - DlgHeight) div 2 ;
SetWindowPos(hWindow,
0 ,
DlgPosX,
DlgPosY,
0 ,
0 ,
SWP_NOSIZE or SWP_NOZORDER);
end ;
end ;
end .
24_ システム関係ダイアログの表示位置やサイズの制御
Windows のシステム関係ダイアログの表示位置を設定する例です.
システム関係のダイアログを表示する関数はありません.ShellExecute 関数等で,別のアプリのダイアログとして表示します.
システムのダイアログに関しては,専用のフックが用意されています.TApplication コンポーネントの HookMainWindow メソッドでフックを開始して,UnhookMainWindow メソッドでフックを終了します.それらを使用したサンプルです.
システムでは,クラス名が #32770 のダイアログが複数起動していることがあります.Windows やアプリケーションのアップデートの案内がダイアログを使用していることがあります.これらは,タスクバーの通知領域 (タスクトレイ) に,アイコンで表示されている場合があります.
自アプリでダイアログを表示すると,そのダイアログは,Z オーダーがトップの,最前面のウインドウとして表示されます.そこで,下のコードでは,Z オーダーがトップのダイアログを検出したら,フラグをセットします.フラグには論理型変数を使用しています.この値が False の時だけ,フックプロシージャー内の処理を実行するようにしています.
この処理をしないと,起動したダイアログを閉じても,他のダイアログにフォーカスが移動してしまい,自アプリがアクティブにできない状態になってしまうことがあります.
システムダイアログに限らず,ダイアログの中にはサイズの変更ができないようにしているものがあります.自身が開発するアプリケーションでも,フォームの位置変更や,サイズの変更ができないようにすることができます.
図45
表示位置を設定したシステムダイアログ
Form1 の左端から 40 ピクセル右,上端から 80 ピクセル下の位置から表示
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class (TForm)
Button1: TButton;
OpenDialog1: TOpenDialog;
ReplaceDialog1: TReplaceDialog;
Button2: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
private
{ Private 宣言 }
FDialogSetPos : Boolean;
FDialogWindow : HWND;
function DialogHook(var Message: TMessage): Boolean;
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses ShellAPI;
{$R *.dfm}
// =============================================================================
// フォーム生成時の処理
// Windowsシステムのダイアログのフックを開始する
// =============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
// ボタンクリックで表示するダイアログだけを操作対象とするためにTrueにしておく
FDialogSetPos := True;
// フック開始
Application.HookMainWindow(DialogHook);
end ;
// =============================================================================
// フォームを閉じる時の処理
// 起動したダイアログを閉じる
// Windowsシステムのダイアログのフックを解除する
// =============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if FDialogWindow <> 0 then begin
PostMessage(FDialogWindow, WM_CLOSE, 0 , 0 );
end ;
Application.UnhookMainWindow(DialogHook);
end ;
// =============================================================================
// Windowsのシステム関係ダイアログを表示する例
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
StrText : String ;
begin
// 表示のフラグをFalseにしておく
FDialogSetPos := False;
FDialogWindow := 0 ;
// [日付と時刻]のダイアログを表示
StrText := ' timedate.cpl' ;
ShellExecute(Handle, 'open' , 'control.exe' , PChar(StrText), nil , SW_SHOW);
end ;
// -----------------------------------------------------------------------------
// TApplication.HookMainWindowの処理関数
// システム関係のダイアログは非表示で複数起動している場合がある
// 最初に見つけたZオーダーがトップのダイアログを処理したらフラグをTrueにする
// -----------------------------------------------------------------------------
function TForm1.DialogHook(var Message: TMessage): Boolean;
var
DlgRect : TRect;
DlgPosX : Integer;
DlgPosY : Integer;
begin
Result := False;
if FDialogSetPos = False then begin
FDialogWindow := FindWindow('#32770' , nil );
// ダイアログのタイトルが分かっている場合は,以下の方が確実
FDialogWindow := FindWindow(nil , '日付と時刻' );
if FDialogWindow <> 0 then begin
if Message.Msg = WM_ACTIVATEAPP then begin
FDialogSetPos := True;
GetWindowRect(FDialogWindow, DlgRect);
DlgPosX := Self.Left + 40 ;
DlgPosY := Self.Top + 80 ;
SetWindowPos(FDialogWindow,
0 ,
DlgPosX,
DlgPosY,
0 ,
0 ,
SWP_NOSIZE or SWP_NOZORDER);
end ;
end ;
end ;
end ;
end .
25_ ダイアログのカスタマイズ例 - InputBox
ダイアログのカスタマイズの例です.PostMessage を使用しています.
位置やサイズの変更もできますが,ここではボタンのキャプションを変更します.
InputBox のクラス名は TForm ですが,外部から TForm のインスタンスは取得できません.そこで,メッセージ処理内で TForm のインスタンスを取得して操作しています.
InputBox や InputQuery ダイアログをカスタマイズする場合,ダイアログのソースコードを参考にして,自前でダイアログを作成してしまった方がいいかも知れません.
[備考]
TOenDialog 等の Windows のコモンダイアログをカプセル化したダイアログのカスタマイズは簡単ではありません.しかし,TForm から作成すれば,自由な仕様のダイアログが作成できます.また,テンプレートを利用することもできます.
TOpenDialog や TSaveDialog,フォルダの参照ダイアログのようなデザインのダイアログも,Delphi に付属のシェルコンポーネント等を使用して作成することができます.
図46
変更前
図47
変更後
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class (TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
protected
procedure WMApp(var Message: TMessage); message WM_APP;
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
{$R *.dfm}
// =============================================================================
// InputBoxダイアログを表示
// メッセージ処理なし
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
InputBox('テスト' ,'テストです' ,'' );
end ;
// =============================================================================
// InputBoxダイアログを表示
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
PostMessage(Handle, WM_APP, 0 , 0 );
InputBox('テスト' ,'テストです' ,'' );
end ;
// =============================================================================
// メッセージ処理
// ダイアログ表示直前にPosMessageでメッセージキューに書き込んでおく
// =============================================================================
procedure TForm1.WMApp(var Message: TMessage);
var
i : Integer;
FrmDlg : TForm;
hOKBtn : HWND;
OKBtn : TWinControl;
begin
FrmDlg := nil ;
// ダイアログのフォームを探す
for i := 0 to Screen.FormCount-1 do begin
if Screen.Forms[i].ClassName = 'TForm' then begin
Frmdlg := Screen.Forms[i];
break;
end ;
end ;
if FrmDlg = nil then exit;
// [OK]ボタンのハンドルを取得して,ハンドルからボタンオブジェクトを取得
hOKBtn := FindWindowEx(FrmDlg.Handle, 0 , 'TButton' , 'OK' );
OKBtn := FindControl(hOKBtn);
// キャプションを変更する
TButton(OKBtn).Caption := '決定' ;
end ;
end .
26_ ダイアログのカスタマイズ例 - MessageDlg
上のコードで操作した InputBox は Delphi で作られています.したがって,コントロールのオブジェクトを取得して操作できました.
Windows が提供している MessageDlg ダイアログは,そのようなことはできません.コントロールのウインドウハンドルを取得して操作することになります.その例です.
MessageDlg 関数で表示するダイアログは,サイズの変更はできません.
このサンプルでは PostMessage メッセージを使用して,FindWindow でダイアログのウィンドウを検出していますが,既にダイアログが表示されていると誤検出することがあります.タスクトレイにある設定用の非表示ダイアログにも影響されます.ローカルフックを使用した方が動作は確実です.
図48
変更前
図49
変更後
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class (TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
protected
procedure WMApp(var Message: TMessage); message WM_APP;
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
{$R *.dfm}
// =============================================================================
// MessageDlgダイアログを表示
// メッセージ処理なし
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
StrText : String ;
begin
StrText := 'ダイアログ関係関数テスト' ;
MessageDlg(StrText, mtInformation, [mbYes, mbCancel, mbAll], 0 );
end ;
// =============================================================================
// MessageDlgダイアログを表示
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
StrText : String ;
begin
StrText := 'ダイアログ関係関数テスト' ;
PostMessage(Handle, WM_APP, 0 , 0 );
MessageDlg(StrText, mtInformation, [mbYes, mbCancel, mbAll], 0 );
end ;
// -----------------------------------------------------------------------------
// ウィンドウのクラス名を取得する関数
// -----------------------------------------------------------------------------
function GetWindowClassNameStr(hWindow: HWND): String ;
var
Buffer : array [0 ..MAX_PATH-1 ] of Char;
Len : Integer;
begin
FillChar(Buffer, SizeOf(Buffer), #0 );
Len := GetClassName(hWindow, Buffer, Length(Buffer));
if Len > 0 then Result := Buffer else Result := '' ;
end ;
// -----------------------------------------------------------------------------
// EnumChildWindowsのコールバック関数
// ここでは,ダイアログの子ウィンドウを列挙
// 子ウインドウのクラス名がButtonだったらキャプションの文字列を変える
// -----------------------------------------------------------------------------
function EnumChildWndTestProc(hWindow: hWnd; lPar: PInteger):
Boolean; Stdcall ;
var
ClassName : String ;
aCaption : String ;
begin
Result := True;
ClassName := GetWindowClassNameStr(hWindow);
if ClassName = 'Button' then begin
lPar^ := lPar^ + 1 ;
if lPar^ = 1 then begin
aCaption := 'YES (&Y)' ;
SendMessage(hWindow, WM_SETTEXT, 0 , LPARAM(PChar(aCaption)));
end else
if lPar^ = 2 then begin
aCaption := 'NO' ;
SendMessage(hWindow, WM_SETTEXT, 0 , LPARAM(PChar(aCaption)));
end else
if lPar^ = 3 then begin
aCaption := '全部 (&A)' ;
SendMessage(hWindow, WM_SETTEXT, 0 , LPARAM(PChar(aCaption)));
end ;
end ;
end ;
// =============================================================================
// メッセージ処理
// ダイアログ表示直前にPosMessageでメッセージキューに書き込んでおく
// =============================================================================
procedure TForm1.WMApp(var Message: TMessage);
var
hWindow : HWND;
BtnNumber : Integer;
DlgRect : TRect;
DlgWidth : Integer;
DlgPosX : Integer;
DlgPosY : Integer;
begin
hWindow := FindWindow('#32770' , nil );
// ウィンドウのタイトルが分かっているなら以下の方が確実
hWindow := FindWindow(nil , '情報' );
if hWindow <> 0 then begin
BtnNumber := 0 ;
// ダイアログの子ウィンドウを列挙
EnumChildWindows(hWindow, @EnumChildWndTestProc, LPARAM(@BtnNumber));
GetWindowRect(hWindow, DlgRect);
// ダイアログの幅と高さを取得
DlgWidth := DlgRect.Right - DlgRect.Left;
// Form1の右の中央.Form1の上端から指定ピクセル下から表示
DlgPosX := Form1.Left + (Form1.Width - DlgWidth) div 2 ;
DlgPosY := Form1.Top + 100 ;
SetWindowPos(hWindow,
0 ,
DlgPosX,
DlgPosY,
0 ,
0 ,
SWP_NOSIZE or SWP_NOZORDER);
end ;
end ;
end .
27_ ダイアログのカスタマイズ例 - フォントの変更
ダイアログのフォントを変更する例です.
Windows のダイアログボックスである MessageBox であれば,ダイアログのコールバック関数内でフォントの色と背景色が変更できます.
次のコードは,コールバック関数を定義するために,ダイアログのフックで,ダイアログボックスが初期化される WM_INITDIALOG メッセージを受信したら,そのダイアログボックスのプロシージャーを設定しています.
フォントの色は,文字列を描画するコントロールのデバイスコンテキストに対して,SetTextColor 関数で設定します.
図50
文字の色を変更した結果
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class (TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
{$R *.dfm}
var
MsgHook : HHOOK;
OldDlgProc : Pointer;
// -----------------------------------------------------------------------------
// ウィンドウのクラス名を取得する関数
// -----------------------------------------------------------------------------
function GetWindowClassNameStr(hWindow: HWND): String ;
var
Buffer : array [0 ..MAX_PATH-1 ] of Char;
Len : Integer;
begin
FillChar(Buffer, SizeOf(Buffer), #0 );
Len := GetClassName(hWindow, Buffer, Length(Buffer));
if Len > 0 then Result := Buffer else Result := '' ;
end ;
// -----------------------------------------------------------------------------
// ダイアログのコールバック関数
// -----------------------------------------------------------------------------
function DialogProc(hdlg: HWND; uiMsg: UINT; wParam:
WPARAM; lParam:LPARAM): UINT; stdcall ;
var
LhdcStatic : HDC;
Begin
if uiMsg = WM_CTLCOLORSTATIC then begin
// クラス名がStaticのデバイスコンテキストを取得
LhdcStatic := wParam;
SetTextColor(LhdcStatic, clGreen);
Result := GetStockObject(WHITE_BRUSH);
end else
if uiMsg = WM_CTLCOLORDLG then begin
Result := GetStockObject(WHITE_BRUSH);
end else begin
Result := CallWindowProc(OldDlgProc, hdlg, uiMsg, wParam, lParam);
end ;
end ;
// -----------------------------------------------------------------------------
// WH_CALLWNDPROCフックのコールバック関数
// ここではMsg : PMsgではなくMsg: PCWPStructにして,wParamの値を受け取る
//
// Windowsのダイアログのクラス名は#32770
// -----------------------------------------------------------------------------
function GetMsgProc(Code: Integer; WPar: WPARAM; LPar: LPARAM): LRESULT; stdcall ;
var
LpMsgStruct : PCWPStruct;
LhWindow : HWND;
LClassName : String ;
begin
if (code < 0 ) then begin
Result := CallNextHookEx(MsgHook, Code, WPar, LPar);
exit;
end ;
if (code = HC_ACTION) then begin
// ウィンドウハンドルを取得
LpMsgStruct := PCWPStruct(LPar);
LhWindow := LpMsgStruct^.hwnd;
LClassName := GetWindowClassNameStr(LhWindow);
if LClassName = '#32770' then begin
// ダイアログのハンドルを取得
LhWindow := LpMsgStruct^.hwnd;
if LpMsgStruct^.message = WM_INITDIALOG then begin
// ダイアログボックスのプロシージャ(コールバック関数)を設定
OldDlgProc := Pointer(GetWindowLong(LhWindow, DWL_DLGPROC));
SetWindowLong(LhWindow, DWL_DLGPROC, Integer(@DialogProc));
end ;
end ;
end ;
Result := CallNextHookEx(MsgHook, Code, WPar, LPar);
end ;
// =============================================================================
// ローカルフック開始
// =============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
MsgHook := SetWindowsHookEx(WH_CALLWNDPROC, GetMsgProc, 0 , GetCurrentThreadID);
end ;
// =============================================================================
// フック解除
// =============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
UnHookWindowsHookEx(MsgHook);
end ;
// =============================================================================
// ダイアログを表示
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
StrText : String ;
begin
StrText := 'ダイアログのフォント色変更' ;
MessageBox(Handle, PChar(StrText), '情報' , MB_ICONINFORMATION);
end ;
end .
フォントのサイズは,コールバック関数内で,デバイスコンテキストに対しても操作できますが,以下のようにダイアログボックスが初期化される時に,WM_SETFONT メッセージで設定できます.
LOGFONT 構造体の各メンバーに設定する内容をセットします.
フォントのサイズを変更した場合は,そのテキスト (文字列) を表示するコントロールのサイズも変更しないと,下図の結果のように,文字列が描画領域でクリップされます.実際には,描画コントロールだけでなく,ダイアログボックスと,ダイアログボックスに配置されているコントロール類の再配置も必要となります.
図51
フォントサイズを変更した結果
copy code
// -----------------------------------------------------------------------------
// WH_CALLWNDPROCフックのコールバック関数
// ここではMsg : PMsgではなくMsg: PCWPStructにして,wParamの値を受け取る
//
// Windowsのダイアログのクラス名は#32770
// -----------------------------------------------------------------------------
function GetMsgProc(Code: Integer; WPar: WPARAM; LPar: LPARAM): LRESULT; stdcall ;
var
LpMsgStruct : PCWPStruct;
LhWindow : HWND;
LClassName : String ;
LhDlgItem : HWND;
LLogFont : TLogFont;
LhFontOld : HFONT;
begin
if (code < 0 ) then begin
Result := CallNextHookEx(MsgHook, Code, WPar, LPar);
exit;
end ;
if (code = HC_ACTION) then begin
// ウィンドウハンドルを取得
LpMsgStruct := PCWPStruct(LPar);
LhWindow := LpMsgStruct^.hwnd;
LClassName := GetWindowClassNameStr(LhWindow);
if LClassName = '#32770' then begin
// ダイアログのハンドルを取得
LhWindow := LpMsgStruct^.hwnd;
if LpMsgStruct^.message = WM_INITDIALOG then begin
// ダイアログボックスのプロシージャ(コールバック関数)を設定
OldDlgProc := Pointer(GetWindowLong(LhWindow, DWL_DLGPROC));
SetWindowLong(LhWindow, DWL_DLGPROC, Integer(@DialogProc));
LhDlgItem := GetDlgItem(LhWindow, $FFFF );
// テキストのフォントハンドルを取得
LhFontOld := SendMessage(LhDlgItem, WM_GETFONT, 0 , 0 );
// そのテキストのTLogFontの値を取得
GetObject(LhFontOld, SizeOf(TLogFont), @LLogFont);
// 論理フォント構造体の一部を変更
LLogFont.lfHeight := 25 ;
LLogFont.lfFaceName := 'MS Pゴシック' ;
// 新しいフォントハンドルに設定したTLogFontを設定
hFontNew := CreateFontIndirect(LLogFont);
// フォントを設定
SendMessage(LhDlgItem, WM_SETFONT, hFontNew, MakeLParam(1 ,0 ));
end ;
end ;
end ;
Result := CallNextHookEx(MsgHook, Code, WPar, LPar);
end ;
// =============================================================================
// ローカルフック開始
// =============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
MsgHook := SetWindowsHookEx(WH_CALLWNDPROC, GetMsgProc, 0 , GetCurrentThreadID);
end ;
// =============================================================================
// フック解除
// hFontNewはグローバル変数(HFONT)
// =============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
DeleteObject(hFontNew);
UnHookWindowsHookEx(MsgHook);
end ;
ダイアログのメッセージの文字列を複数行にする場合は,改行文字を使用します.ただし,改行文字を無視するダイアログもあります.
TaskMessageDlg 関数では,タイトルとして,別のテキストを表示することができます.
図52
メッセージの複数行表示
copy code
procedure TForm1.Button1Click(Sender: TObject);
var
StrText : String ;
begin
StrText := 'ダイアログ関係関数テスト' + sLineBreak + '2 行目のテキスト' ;
Dialogs.ShowMessage(StrText);
end ;
Windows XP までは,SHFileOperation 関数を使用してファイルのコピー,削除等を実行すると,進捗状況を表示するダイアログに,それらの操作に対応するアニメーションが表示されました.
Windows Vista 以降では,このアニメーションは表示されません.
図53
SHFileOperation によるファイル操作
Windows XP の場合
ファイル操作に応じたアニメーションが表示される
下のコードはフォルダのコピー操作となっている
図54
SHFileOperation によるファイル操作
Windows 7 の場合
アニメーションの表示はない
copy code
implementation
uses
ShellAPI;
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
LSourceDir : String ;
LDestDir : String ;
LSHStruct : TSHFileOpStruct;
begin
LSourceDir := 'D:\000\100' ;
LDestDir := 'D:\000\200' ;
// 構造体の初期設定
LSHStruct.wnd := Handle;
LSHStruct.wFunc := FO_COPY;
LSHStruct.pFrom := PChar(LSourceDir);
LSHStruct.pTo := PChar(LDestDir);
LSHStruct.fFlags := FOF_MULTIDESTFILES;
// 実行する
SHFileOperation(LSHStruct);
end ;
Windows Vista 以降でアニメーションを表示するには,自前でダイアログを作成します.
そのダイアログで TAnimate コンポーネントを使用して,表示するアニメーションの AVI ファイルを指定します.
TShellResources コンポーネントを使用すると,あらかじめ用意されている,ファイルのコピーや削除などの AVI ファイルを指定して表示することができます.下図はその例です.
TShellResources コンポは,Delphi 2007 以降に実装されています.
図55
設計時画面
破線が TAnimate コンポーネント
TShellResources が必要
uses に ShellAnimations を追加するだけでもよい
図56
[ファイルのコピー] のアニメーション
CommonAVI の値は オブジェクトインスペクタからも設定可能
図57
[ゴミ箱を空にする] アニメーション
copy code
procedure TForm1.Button1Click(Sender: TObject);
begin
Animate1.Active := False;
Animate1.AutoSize := True;
Animate1.CommonAVI := aviCopyFiles;
Animate1.Active := True;
end ;
procedure TForm1.Button2Click(Sender: TObject);
var
LHeight : Integer;
LWidth : Integer;
begin
Animate1.Active := False;
Animate1.CommonAVI := aviEmptyRecycle;
LHeight := Animate1.FrameHeight;
LWidth := Animate1.FrameWidth;
Animate1.AutoSize := False;
Animate1.Width := LWidth;
Animate1.Height := LHeight;
Animate1.Active := True;
end ;
カスタマイズしたダイアログを TForm から自作する場合,ダイアログに使用する画像は,システムのリソースや DLL から取得することができます.
以下のリンクの記事を参考にしてください.
31_ コントロールのクラス名や ID 値等を調べる方法
ダイアログに限らず,対象のウインドウにどのようなコントロールが配置されているかを調べるには,例えば,サンプルプログラム集の [645_ウィンドウの階層のツリー表示] に掲載しているサンプルプログラムで調べることができます.
このサンプルで知ることができるのは,ウィンドウハンドルを持つコントロールだけです.
[備考]
コントロールの ID 値は,ウィンドウハンドルと同じく,生成される度に新しい値となります.
ID 値を固定した値にするには,SetWindowLong 関数を使用して,実行時に設定します.
Windows のコモンダイアログの多くは,ダイアログに配置しているコントロールの ID 値が一定の値になっています.