Delphi Programming / Object Pascal
[掲載 2010年01月05日] [更新 2017年03月14日] Delphi サンプルプログラム集
127_フォームとシステムメニュー操作関係
動作確認等
Windows 7 U64(SP1) + Delphi XE(UP1) Pro
127_FormSystemMenuETC.zip [7,856 KB] 2017年03月14日版 (EXE 同梱)
2012年01月14日
Delphi XE で EXE を再作成.コード整形と一部のサンプルにコード追加
2017年03月14日
フォームを使用しない,フォームを非表示と,フォームの移動リサイズ関係のサンプルを,新規に作成したページに移動
フォームのシステムメニューの削除や追加変更等と,それと関係しているかも知れないフォームの操作関係の参考コードです.
[備考]
フォームの [最小化] [最大化] は,フォームのサイズが変わるわけですから,フォームのリサイズ関係の制御と言えます.
フォームの [最小化] [最大化] [閉じる] ボタンは,プロパティ BorderIcons で無効にできますが,このサンプルは GetSystemMenu 関数と DeleteMenu 関数を使用して,フォームのシステムメニューを削除して非表示にし,その機能を使用できないようにします.ただし,[閉じる] ボタンは非表示にはできません.
図1
設計時画面
図2
実行開始後のメニュー
図3
Button1 クリック後のメニュー
図4
Button2 クリック後のメニュー
[サイズ変更] が削除されている
フォームのサイズが変更不可となる
図5
Button3 クリック後のメニュー
[閉じる]が削除されている
ただし [×] ボタンは無効になるだけ
非表示にはできない
閉じるには [Alt] + [F4] で
図6
タスクバーのメニューも同じ
BorderIcons プロパティの biSystemMenu を False にすると,システムの閉じるボタン [X] を非表示にできますが,下図のように,アイコンも表示しなくなります.また,最大化,最小化のボタン等も表示しなくなります.
図7
BorderIcons に biSystemMenu がない場合
copy code
unit FormSystemMenuDeleteUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class (TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
{$R *.dfm}
// =============================================================================
// 上から1番目のシステムメニューを削除
// メニュー項目の位置( MF_BYPOSITION)ではなく,メニュー名で指定する場合は
// DeleteMenu(hSysMenu, SC_MOVE, MF_BYCOMMAND);
// のようにする(Button3Clickイベントのコード参照)
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
hSysmenu : hMenu;
begin
// フォームのシステムメニューのハンドルを取得
hSysmenu := GetSystemMenu(Handle, False);
// 上から1番目のメニューを削除
DeleteMenu(hSysMenu, 0 , MF_BYPOSITION);
end ;
// =============================================================================
// 上から2番目のシステムメニューを削除
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
hSysmenu : hMenu;
begin
// フォームのシステムメニューのハンドルを取得
hSysmenu := GetSystemMenu(Handle, False);
// 上から2番目のメニューを削除
DeleteMenu(hSysMenu, 1 , MF_BYPOSITION);
end ;
// =============================================================================
// SC_CLOSE(閉じる)メニューを削除
//
// 参考
// [閉じるボタン]は削除できない.非表示にもできない
// =============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
hSysmenu : hMenu;
AItemCnt : Integer;
begin
// フォームのシステムメニューのハンドルを取得
hSysmenu := GetSystemMenu(Handle, False);
// 閉じるメニューを無効化
DeleteMenu(hSysMenu, SC_CLOSE, MF_BYCOMMAND);
// 現在のメニューの項目数を取得して一番下の区切り線を削除
AItemCnt := GetMenuItemCount(hSysmenu);
DeleteMenu(hSysmenu, AItemCnt - 1 , MF_BYPOSITION);
end ;
end .
このサンプルは GetSystemMenu 関数と InsertMenuItem 関数を使用して,フォームのシステムメニューを追加します.
図8
設計時画面
図9
実行開始後のメニュー
図10
ボタンクリック後のメニュー
[追加メニュ] (上から2番目)
[テストメニュー] (下から2番目)
[テスト項目] (一番下)
が追加されている
[テストメニュー] はメニュー表示開始時に淡色に設定している
図11
タスクバーも同じメニュー
図12
[テスト項目] をクリック
copy code
unit FormSystemMenuInsertUnit;
interface
uses
{$IF CompilerVersion > 22.99}
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls;
{$ELSE}
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
{$IFEND}
type
TForm1 = class (TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
protected
procedure WMSysCommand(var Message: TWMSysCommand); message WM_SYSCOMMAND;
procedure WMInitMenuPopup(var Message: TWMInitMenuPopup);
message WM_INITMENUPOPUP;
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
// 追加メニューのID値
// 値は任意の整数値
const
MyMenu1 = 101 ;
MyMenu2 = 102 ;
MyMenu3 = 103 ;
{$R *.dfm}
// =============================================================================
// システムメニューを追加
// InsertMenu関数の第3引数にはいろいろ値がある
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
hSysmenu : hMenu;
AItemCnt : Integer;
begin
// フォームのシステムメニューのハンドルを取得
hSysmenu := GetSystemMenu(Handle, False);
// 現在の項目数を取得
AItemCnt := GetMenuItemCount(hSysmenu);
// 現在のメニュー一番下に区切り線(第4,5引数を0にする)を追加
InsertMenu(hSysmenu, AItemCnt, MF_BYPOSITION, 0 , nil );
// 現在のメニューの一番下にメニューを追加
InsertMenu(hSysmenu, AItemCnt + 1 , MF_BYPOSITION, MyMenu1, 'テストメニュー' );
// 現在のメニューの一番下にメニューを追加
InsertMenu(hSysmenu, AItemCnt + 2 , MF_BYPOSITION, MyMenu2, 'テスト項目' );
// [移動]メニュー(SC_MOVE)の前に[追加メニュー]を追加
// 操作不可にして淡色表示に
InsertMenu(hSysmenu, SC_MOVE, MF_BYCOMMAND or
MF_DISABLED or
MF_GRAYED, MyMenu3, '追加メニュー' );
end ;
// =============================================================================
// メニューを選択した時のメッセージ処理
// =============================================================================
procedure TForm1.WMSysCommand(var Message: TWMSysCommand);
begin
case Message.CmdType of
MyMenu1 : Showmessage('テストメニューです. ' );
MyMenu2 : Showmessage('テスト項目です. ' );
end ;
inherited ;
end ;
// =============================================================================
// メニューを表示する時のメッセージ処理
// 追加したMyMenu1をグレイ表示にして使用不可とする
// =============================================================================
procedure TForm1.WMInitMenuPopup(var Message: TWMInitMenuPopup);
begin
inherited ;
if Message.SystemMenu then
begin
EnableMenuItem(Message.MenuPopup, MyMenu1, MF_BYCOMMAND or MF_GRAYED);
end ;
end ;
end .
03_ キャプションバー ( タイトルバー ) のマウスイベント
キャプションバーをマウスでクリックした時に,何かを処理したい場合の参考です.このサンプルでは,押下したマウスボタンによってフォームの色が変化するようにしています.
キャプションバー (タイトルバー) 等の非クライアント領域のメッセージには WM_NC 系のメッセージが利用できます.
キャプションバーの最小化や最大化等のアイコン類のクリックを検出する方法は,後述の [08_アプリのアイコン・最小化・最大化・閉じるボタンのメッセージ]
を参考にしてください.
図13
設計時画面
図14
マウスホイールをクリック
リスト3
キャプションバー (タイトルバー) のマウスイベント
copy code
unit CaptionBarMouseEventUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TForm1 = class (TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
protected
procedure WMNCLButtonDown(var Message: TWMNCLButtonDown);
message WM_NCLBUTTONDOWN;
procedure WMNCRButtonDown(var Message: TWMNCRButtonDown);
message WM_NCRBUTTONDOWN;
procedure WMNCLButtonDblClk(var Message: TWMNCLButtonDblClk);
message WM_NCLBUTTONDBLCLK;
procedure WMNCMButtonDown(var Message: TWMNCMButtonDown);
message WM_NCMBUTTONDOWN;
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
{$R *.dfm}
// =============================================================================
// [終了]ボタン
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
Close;
end ;
// =============================================================================
// マウス左ボタンダウン
// WM_NCLBUTTONDOWNメッセージ
// =============================================================================
procedure TForm1.WMNCLButtonDown(var Message: TWMNCLButtonDown);
begin
Self.Color := clRed;
// 元の動作を実行
// これを削除すれば動作無効
inherited ;
end ;
// =============================================================================
// マウス右ボタンダウン
// WM_NCRBUTTONDOWNメッセージ
// =============================================================================
procedure TForm1.WMNCRButtonDown(var Message: TWMNCRButtonDown);
begin
Self.Color := clGreen;
// 元の動作を実行
// これを削除すれば動作無効
inherited ;
end ;
// =============================================================================
// マウス左ボタンダブルクリック
// inheritedを削除して元の動作(最大化と元のサイズに戻すを無効に
// WM_NCLBUTTONDBLCLKメッセージ
// =============================================================================
procedure TForm1.WMNCLButtonDblClk(var Message: TWMNCLButtonDblClk);
begin
Self.Color := clBlue;
end ;
// =============================================================================
// マウス中央(ホイール)ボタンダウン
// WM_NCMBUTTONDOWNメッセージ
// =============================================================================
procedure TForm1.WMNCMButtonDown(var Message: TWMNCMButtonDown);
begin
Self.Color := clYellow;
// 元の動作を実行
// これを削除すれば動作無効
inherited ;
end ;
end .
フォームの表示が終了した (画面に見えた) 時点で何かを処理したい場合の参考コードです.ここでは単にダイアログを表示するだけとなっています.
図15
設計時画面
図16
実行開始後
OnShow 内で表示しているが.フォームは表示されていない
図17
フォーム表示終了後のダイアログ
copy code
unit FormShowingChangedUnit;
interface
uses
{$IF CompilerVersion > 22.99}
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls, Vcl.ExtCtrls;
{$ELSE}
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
{$IFEND}
type
TForm1 = class (TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormShow(Sender: TObject);
private
{ Private 宣言 }
protected
procedure CMShowingchanged(var Message: TMessage);
message CM_SHOWINGCHANGED;
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
{$R *.dfm}
// =============================================================================
// [終了]ボタン
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
Close;
end ;
// =============================================================================
// フォーム表示後のメッセージ
//
// CM_SHOWINGCHANGEDメッセージを処理する
// CM_SHOWINGCHANGEDは表示の状態が変化した時に発生する
// 非表示になる時も発生するので,Visibleプロパティで判断する
// =============================================================================
procedure TForm1.CMShowingchanged(var Message: TMessage);
begin
// 元のメッセージを処理
inherited ;
if Visible then begin
Update;
// 表示後の処理
ShowMessage('フォーム表示終了しました. ' );
end ;
end ;
// =============================================================================
// フォーム表示中のイベント
// =============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
ShowMessage('フォーム表示中です. ' );
end ;
end .
05_ フォームの背景 ( TCanvas ) をグラデーション描画
GraphUtil ユニットには追加のグラフィック処理関係の関数類があります.これを利用して,フォームの背景をグラデーションで塗り潰す例です.ついでに半透明にしてみました.半透明はフォームのプロパティ (このサンプルではオブジェクト・インスペクタ) で設定します.
Windows API に GradientFill という似た名前の関数がありますが,こちらは長方形と三角形の塗りつぶし用の関数です.
図18
設計時画面
図19
実行結果
copy code
unit GradientFormUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TForm1 = class (TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormPaint(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses GraphUtil;
{$R *.dfm}
// =============================================================================
// [終了]ボタン
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
Close;
end ;
// =============================================================================
// フォームの背景をグラデーションに
// フォームのCanvasに描画したものはOnPaintに描画しないと他のウィンドウに隠され
// た際に消去されてしまう
// そこで,このサンプルでは,OnPaintで処理して,常に描画が行われるようにして
// いる
//
// usesにGraphUtilが必要
// AEndColorをclGradientInactiveCaptionにするとデフォルトのグラデーション配色,
// =============================================================================
procedure TForm1.FormPaint(Sender: TObject);
var
Rect: TRect;
begin
Rect := Self.ClientRect;
GradientFillCanvas(Canvas, clWhite, clBlue, Rect, gdVertical);
end ;
end .
フォームあるいはアプリケーションを終了する時に,どんなイベントやメッセージを通過するかを調査するためのテストプログラムです.終了するとプロジェクトフォルダ内の Close.txt にその記録を保存します.
フォームを閉じる時のイベントには,OnCloseQuery と OnClose がありますが,OnCloseQuery の方が早く発生します.この OnCloseQuery イベントの CanClose 引数の値を False にするとフォームを閉じなくすることができます.False のままだと,強制終了しない限り永遠に閉じませんから,何かしらの条件で CanClose を True にするようにします.
[備考]
本サンプルでは,タスクマネージャ等で強制終了された場合のメッセージは受信できません.
WMI 関係のイベントを利用すると,タスクマネージャー等での強制終了も検出できます.
図20
設計時画面
図21
フォームを閉じると Close.txt ができる
図22
[Alt] + [F4] で終了した場合
リスト6
フォームを閉じた時のイベントとメッセージを調査
copy code
unit FormColseMessageUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, AppEvnts;
type
TForm1 = class (TForm)
Button1: TButton;
Button2: TButton;
Button3: TButton;
ApplicationEvents1: TApplicationEvents;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure FormCloseQuery(Sender: TObject; var CanClose: Boolean);
procedure ApplicationEvents1Activate(Sender: TObject);
procedure ApplicationEvents1Deactivate(Sender: TObject);
procedure ApplicationEvents1ShortCut(var Msg: TWMKey; var Handled: Boolean);
private
{ Private 宣言 }
SL : TStringList;
protected
procedure WMSysCommand(var Message: TWMSysCommand); message WM_SYSCOMMAND;
procedure WMQueryEndSession(var Message: TWMQueryEndSession);
message WM_QUERYENDSESSION;
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
{$R *.dfm}
// =============================================================================
// TStringList作成
// =============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
SL := TStringList.Create;
end ;
// =============================================================================
// FormのOnCloseQuery
// =============================================================================
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
SL.Add('OnCloseQuery' );
end ;
// =============================================================================
// FormのOnClose
// =============================================================================
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
SL.Add('OnClose' );
end ;
// =============================================================================
// 記録内容をファイルに保存
// =============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
SL.Add('OnDestroy' );
SL.SaveToFile('Close.txt' );
FreeAndNil(SL);
end ;
// =============================================================================
// [Close]ボタン
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
Close;
end ;
// =============================================================================
// [WM_CLOSE]ボタン
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
PostMessage(Handle, WM_CLOSE, 0 , 0 );
end ;
// =============================================================================
// [Terminate]ボタン
// OnDestroyしか発生しない
// =============================================================================
procedure TForm1.Button3Click(Sender: TObject);
begin
Application.Terminate;
end ;
// =============================================================================
// TApplicationEvetnコンポのOnActivate
// =============================================================================
procedure TForm1.ApplicationEvents1Activate(Sender: TObject);
begin
SL.Add('TApplicationEvent OnActivate' );
end ;
// =============================================================================
// TApplicationEvetnコンポのOnDeactivate
// =============================================================================
procedure TForm1.ApplicationEvents1Deactivate(Sender: TObject);
begin
SL.Add('TApplicationEvent OnDeactivate' );
end ;
// =============================================================================
// TApplicationEvetnコンポのOnShortCut
//
// ショートカット動作の検出
// ここでは[Alt]+[F4]だけを調べる
// =============================================================================
procedure TForm1.ApplicationEvents1ShortCut(var Msg: TWMKey;
var Handled: Boolean);
var
SS : TShiftState;
Begin
SS := KeyDataToShiftState(Msg.KeyData);
if (Msg.CharCode=VK_F4) and (ssAlt in SS) then begin
SL.Add('[Alt]+[F4]' );
end ;
end ;
// =============================================================================
// WM_SYSCOMMANDメッセージ
// [Close]ボタンで終了した時は発生しない
// =============================================================================
procedure TForm1.WMSysCommand(var Message: TWMSysCommand);
var
AType : Integer;
begin
AType :=Message.CmdType and $FFF0 ;
if AType = SC_CLOSE then begin
SL.Add('SC_CLOSE' );
end else
if AType = SC_MOUSEMENU then begin
SL.Add('SC_MOUSEMENU' );
end else
if AType = SC_KEYMENU then begin
SL.Add('SC_KEYMENU' );
end ;
inherited ;
end ;
// =============================================================================
// WM_QUERYENDSESSIONメッセージ
// Windowsを終了する時に発生するメッセージ
// =============================================================================
procedure TForm1.WMQueryEndSession(var Message: TWMQueryEndSession);
begin
SL.Add('WM_QUERYENDSESSION' );
// 終了させる
Message.Result := 1 ;
end ;
end .
07_ [ Alt ] + [ F4 ] で閉じないフォーム
フォーム,ウインドウはシステムメニューにあるように,[Alt] + [F4] で閉じることができます.これを禁止するコードです.単にメッセージを処理するだけですが,メッセージを検出したら,このメッセージを送らないような値なりプロパティをセットします.
このサンプルは TApplication の OnMessage イベントでメッセージ処理を行う例です.
図23
設計時画面
リスト7
[Alt] + [F4] で閉じないウィンドウ
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls, AppEvnts;
type
TForm1 = class (TForm)
ApplicationEvents1: TApplicationEvents;
procedure ApplicationEvents1Message(var Msg: tagMSG; var Handled: Boolean);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
{$R *.DFM}
// =============================================================================
// [Alt]+[F4]でフォームを閉じるのを禁止
// TApplicationのOnMessageでメッセージ処理する場合
// Handled := Trueにするとメッセージ送信しないことを利用
// =============================================================================
procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG;
var Handled: Boolean);
begin
if (Msg.message = WM_SYSKEYDOWN) then begin
if (Msg.wParam = VK_F4) and (GetKeyState(VK_MENU) < 0 ) then begin
Handled := True;
end ;
end ;
end ;
end .
メッセージは,いろいろな方法で捕捉することができます.下のコードは WM_SYSKEYDOWN メッセージで [Alt} + [F4] を検出して,フォームが閉じるのを禁止します.
検出したキーボードのメッセージ データを KeyDataToShiftState 関数で TShiftState 型に変換して [Shift] または [Ctrl] キーの場合は通常のメッセージ処理とします.
リスト8
[Alt] + [F4] で閉じないウィンドウ
copy code
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class (TForm)
private
{ Private 宣言 }
procedure WMSysKeyDown(var Msg : TWMSysKeyDown); Message WM_SYSKEYDOWN;
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
{$R *.DFM}
// =============================================================================
// [Alt]+[F4]でフォームを閉じるのを禁止
// WM_SYSKEYDOWNを使用した例
// =============================================================================
procedure TForm1.WMSysKeyDown(var Msg: TWMSysKeyDown);
begin
// Alt+F4が押された場合
if (Msg.CharCode = VK_F4) then begin
// [Shift]と[Ctril]キーが同時に押されている場合は通常(inherited)動作とする
if (ssShift in KeyDataToShiftState(Msg.KeyData)) or
(ssCtrl in KeyDataToShiftState(Msg.KeyData)) then begin
inherited
end ;
end else begin
inherited ;
end ;
end ;
end .
08_ フォームのアイコン・最小化・最大化・閉じるボタンのメッセージ
システムメニューのアイコン (アプリケーションアイコン),最小化ボタン,最大化ボタン,閉じるボタン (× ボタン) クリックの検出サンプルです.
これらのボタンをクリックした時の専用のイベントはありません.WindowProc や WndProc 等のメソッドあるいは TApplicationEvents の OnMessage イベント等を使用します.
ここでは非クライアント領域のメッセージである WM_NC 系のメッセージで捕捉します.
本サンプルでは WM_NCLBUTTONDOWN メッセージを使用していますから,実際にはマウスの左ボタンを押した時を検出しています.マウスの右ボタンを押した時を検出するには WM_NCRBUTTONDOWNメッセージを使用します.
図24
設計時画面
図25
実行して閉じるボタン (×ボタン) クリック
図26
最小化ボタンをクリック
図27
システムメニューのアイコン (アプリケーションアイコン) をクリック
copy code
unit Unit1;
interface
uses
{$IF CompilerVersion > 22.99}
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs,
Vcl.StdCtrls, Vcl.ExtCtrls, Vcl.AppEvnts;
{$ELSE}
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, AppEvnts;
{$IFEND}
type
TForm1 = class (TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
protected
procedure WMNCLButtonDown(var Message: TWMNCLButtonDown);
message WM_NCLBUTTONDOWN;
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
{$R *.dfm}
// =============================================================================
// [Close]ボタン
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
Close;
end ;
// =============================================================================
// WM_NCLBUTTONDOWNメッセージの処理
// WM_NCLBUTTONDOWNは非クライアント領域でのマウスの左ボタンダウンメッセージ
// マウスのボタンダウンの検出はWM_NCRBUTTONDOWNメッセージを使用する
// =============================================================================
procedure TForm1.WMNCLButtonDown(var Message: TWMNCLButtonDown);
var
LHitTest : Integer;
LText : String ;
begin
LHitTest := Message.HitTest;
if LHitTest = HTCLOSE then begin
LText := 'x ボタンが押されました' + sLineBreak + '本当に閉じますか' ;
if MessageBox(Handle, PChar(LText), '確認' , MB_YESNO) = IDYES then begin
// inherited;だとダイアログのWM_NCLBUTTONDOWNメッセージが発生するので閉じない
Close;
end else begin
Message.Result := 0 ;
end ;
end else
if LHitTest = HTMINBUTTON then begin
ShowMessage('最小化ボタンは機能しません' );
Message.Result := 0 ;
end else
if LHitTest = HTMAXBUTTON then begin
Width := 300 ;
Height := 300 ;
Position := poDesktopCenter;
Message.Result := 0 ;
end else
if LHitTest = HTSYSMENU then begin
LText := 'システムメニュー (アプリケーション) アイコンが押されました' + sLineBreak
+ 'マウスの左ボタンではメニューを表示しないようにしています' + sLineBreak
+ 'システムメニューを表示するにはマウスの右ボタンを押してください' ;
MessageBox(Handle, PChar(LText), '確認' , MB_OK);
Message.Result := 0 ;
end else begin
inherited ;
end ;
end ;
end .
TApplicationEvents コンポーネントを使用することもできます.TApplicationEvents コンポーネントの OnMessage イベントを使用する場合は,例えば次のようにします.
イベントの Handled 引数の値を True にすれば,ここに書いたコード以外の処理は実行しません.つまり,処理を終了したという意味になります.
copy code
// =============================================================================
// [Close]ボタン
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
Close;
end ;
// =============================================================================
// ApplicationEvents1 の OnMessage イベント処理
// =============================================================================
procedure TForm1.ApplicationEvents1Message(var Msg: tagMSG;
var Handled: Boolean);
var
LMsg : Cardinal;
LHitTest : Integer;
begin
LMsg := Msg.message;
// マウスの右と左のどちらのボタン押下でもシステムメニューを表示しない
if (LMsg = WM_NCLBUTTONDOWN) or (LMsg = WM_NCRBUTTONDOWN) then begin
LHitTest := Msg.wParam;
if LHitTest = HTSYSMENU then begin
Handled := True;
end ;
end ;
// [Alt] キーと同時に VKF4 が押されている時は無効にする
if (LMsg = WM_SYSKEYDOWN) then begin
if (Msg. wParam = VK_F4) then begin
Handled := True;
end ;
end ;
// 閉じるボタンでは閉じないようにする
// 最小化ボタンで最小化できないようにする
// 最大化ボタンで最大化できないようにする
if LMsg = WM_NCLBUTTONDOWN then begin
LHitTest := Msg.wParam;
if LHitTest = HTCLOSE then begin
Handled := True;
end else
if (LHitTest = HTMINBUTTON) or (LHitTest = HTMAXBUTTON) then begin
Handled := True;
end ;
end ;
// Caption バーのダブルクリックの操作を無効にする
// つまり最大化できない
if LMsg = WM_NCLBUTTONDBLCLK then begin
LHitTest := Msg.wParam;
if LHitTest = HTCAPTION then begin
Handled := True;
end ;
end ;
end ;
フォームを閉じる時に確認するだけであれば,次のようなコードで十分です.
copy code
// =============================================================================
// [Close]ボタン
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
Close;
end ;
// =============================================================================
// フォームのOnCloseQueryイベント処理
// =============================================================================
procedure TForm1.FormCloseQuery(Sender: TObject; var CanClose: Boolean);
begin
if MessageBox(Handle, '本当に閉じますか' , '確認' , MB_YESNO) = IDYES then begin
CanClose := True;
end else begin
CanClose := False;
end ;
end ;
常に Z オーダーのトップ,つまり最前面に表示するウィンドウにするための 1 つの方法です.
他のアプリケーションの開発者が同じことを考えて,そのアプリケーションのウィンドウを常に再前面に表示するようにするかも知れません.その場合,動作保証はありません.
最前面に表示できるウィンドウは常に 1 つだけです.
対象のフォームを最前面にして,アクティブにするだけであれば,以下のコードで可能です.必要に応じて引数のフラグの値を変更します.
// このフォームを最前面にする
SetWindowPos(Self.Handle,
HWND_TOPMOST,
0 ,
0 ,
0 ,
0 ,
SWP_NOMOVE or SWP_NOSIZE);
[備考]
ShellExecuteEx 関数や CreateProcess 関数で他のアプリを起動し,そのアプリが終了した時に,起動元の自アプリのフォームを最前面に表示する方法については,以下の記事のサンプルを参考にしてください.
図28
設計時画面
リスト10
常に最前面 ( Z オーダーのトップ) に表示するフォーム
copy code
unit TopMostFormUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TForm1 = class (TForm)
Panel1: TPanel;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
OriginalProc:TWndMethod;
procedure SubClassProc(var Message:TMessage);
end ;
var
Form1: TForm1;
implementation
{$R *.dfm}
// =============================================================================
// 元のWindowsProcを退避して新しいWindowProcを定義
// この設定は用途によってはOnShowで設定する必要があるかも知れない
// OnShow設定の場合は,OnCloseでWindowProcを元に戻しておいた方がいいだろう
// =============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
OriginalProc := Self.WindowProc;
Self.WindowProc := SubClassProc;
end ;
// =============================================================================
// WindowsProcを元に戻しておく
// このサンプルではOnCreateでWindowProcを設定しているので実際には不要
// =============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
Self.WindowProc := OriginalProc;
end ;
// =============================================================================
// WindowProcのサブクラス関数
// このフォームがZオーダのトップかを監視して設定
// =============================================================================
procedure TForm1.SubClassProc(var Message: TMessage);
// Targetが最前面かを判定する関数
function IsTopMost(Target: HWND): boolean;
begin
Result := ((GetWindowLong(Target, GWL_EXSTYLE) and WS_EX_TOPMOST) <> 0 );
end ;
// ---------------------------------------------------------------------------
begin
// 元のWindowProcを実行
OriginalProc(Message);
// このフォームが最前面でなければ最前面にする
if IsTopMost(Self.Handle)=False then begin
SetWindowPos(Self.Handle,
HWND_TOPMOST,
0 ,
0 ,
0 ,
0 ,
SWP_NOACTIVATE or SWP_NOMOVE or SWP_NOSIZE or SWP_NOOWNERZORDER);
end ;
end ;
end .
メインフォームである Form1 にオーバラップして Form2 を表示します.Form2 はフォーム生成時に自動的に呼ばれる CreateParams を使用してウィンドウスタイルを変更します.
フォーカスが Form1 に移動しても,Form1 にオーバラップして表示されます.[126_ コード補完ウィンドウもどき] でも似たような動作をするサンプルがあります.
本サンプルのコードのままでは,メインフォームを移動しても,オーバラップ表示しているフォームは移動しません.移動したら,閉じるとか,同時に移動するとかの処理が必要です.
図29
メインフォームの設計時画面
図30
オーバーラップするフォーム
図31
実行画面
[開く] ボタンでForm2表示
Edit1 にフォーカスしても Form1 の上に表示
リスト11
メインフォームのコード
位置を指定して Form2 を表示するだけ
copy code
unit OverLapMainUnit;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
type
TForm1 = class (TForm)
Button1: TButton;
Edit1: TEdit;
procedure Button1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
{$R *.DFM}
uses OverLapForm;
// =============================================================================
// 位置を指定してForm2を表示
// Form2のPositionプロパティはpoDesigned
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
APos : TPoint;
begin
APos := Point(Edit1.Left, Edit1.Top + Edit1.Height);
APos := Form1.ClientToScreen(APos);
Form2.Left := APos.X;
Form2.Top := APos.Y;
Form2.Show;
end ;
end .
リスト12
Form1 にオーバーラップして表示するフォーム
copy code
unit OverLapForm;
interface
uses
{$IF CompilerVersion > 22.99}
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
{$ELSE}
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
{$IFEND}
type
TForm2 = class (TForm)
protected
procedure CreateParams(var Params: TCreateParams); override ;
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form2: TForm2;
implementation
{$R *.DFM}
uses OverLapMainUnit;
// =============================================================================
// オーバーラップなフローティングウィンドウにする
// このフォームを表示した後,Form1にフォーカス移動してもこのフォームはForm1の
// 前面に表示される
//
// このフォームのPositionプロパティはpoDesigned
// =============================================================================
procedure TForm2.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.Style := Params.Style or WS_OVERLAPPED;
Params.WndParent := Form1.Handle;
end ;
end .
オーバラップなフォームのスタイルを次のようにすると,下図のようになります.
必要な時に項目選択用のオーバラップウィンドウを表示するようにします.そして,オーバラップするフォームに,TListBox 等を配置して,[↓] [↑] キーで項目を選択したら,Edit1 にその選択項目を表示するという用途に使えます.
オーバラップなウィンドウを開く時と閉じる時のキー操作の処理が必要かも知れませんが,閉じる方は [ESC] キーでもいいかも知れません.
copy code
procedure TForm2.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.Style := WS_OVERLAPPED or WS_POPUP or WS_THICKFRAME;
Params.WndParent := Form1.Handle;
end ;
図32
オーバーラップなウィンドウの例
オーバラップなフォームのスタイルを次のようにすると,下図のようになります.
こちらの方がフォームに配置した時の TListBox のデザインに近いかも知れません.
copy code
procedure TForm2.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
Params.Style := WS_OVERLAPPED or WS_POPUP or WS_BORDER;
Params.WndParent := Form1.Handle;
end ;
図33
オーバーラップなウィンドウの例
11_ 実行時にフォームのスタイルを変更 - フレーム枠なしウィンドウ
フォームのスタイルは BorderStype,BorderIcons で変更できます.これらは実行時にも変更することができます.この他,SetWindowLong 関数を使用すると,第2引数に GWL_STYLE または GWL_EXSTYLE を指定することで詳細なスタイルの変更が可能です.その例です.スタイルに指定可能な値は Halbow 資料館にもあります.
Windows XP までは,スタイルを変更すると,画面に表示するサイズが変わりましたが,Vista 以降では表示サイズが変わらなくなったようです.
図34
設計時画面
図35
WS_CAPTION のチェックを外した場合
図36
同じ設定で Windows XP の場合
Vista 以降はサイズ変更枠等のデザインが変更となっている
図37
Windows 7 で上と同じスタイルにする
[WS_THICKFRAME] のチェックを外す
Windows XP とはサイズが違うことに注意
Windows XP までは BorderStype を bsNone にするとフレームのないフォーム (ウィンドウ) を作成できました.Widows Vista 以降では,上の図のように,フレームが表示されてしまいます.
Windows Vista, 7 でフレーム枠のないフォームを作成するには,スタイルを WS_POPUP にします.
WS_POPUP というウインドウスタイルは,タイトルバーや枠を持たないウインドウ,つまり,クライアント領域だけで構成されるウインドウのことです.このようなウインドウは,ポップアップウインドウと呼ばれています.
ポップアップウィンドウで全画面 (フルスクリーン) 表示する時は,そのウィンドウを Z オーダーの最前面しないと,タスクバーが表示されてしまいます.
ポップアップウィンドウは,タイトルバー (キャプションバー) がないので,そのままでは終了することができません.何らかのキー操作を検出して処理する方法を考える必要があります.
図38
フレーム枠が完全にないウィンドウ
図39
細線枠のウィンドウ
WS_POPUP にして [WS_BORDER] を含めると,フラットな細線のフレームとなる
copy code
unit Unit1;
interface
uses
{$IF CompilerVersion > 22.99}
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
{$ELSE}
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;
{$IFEND}
type
TForm1 = class (TForm)
GroupBox1: TGroupBox;
CheckBox1: TCheckBox;
CheckBox2: TCheckBox;
CheckBox4: TCheckBox;
CheckBox5: TCheckBox;
CheckBox3: TCheckBox;
CheckBox6: TCheckBox;
CheckBox7: TCheckBox;
procedure FormCreate(Sender: TObject);
procedure CheckBox1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
{$R *.DFM}
// =============================================================================
// フォーム作成時の処理
// 現在の設定状態をチェックボックスの状態に反映
// =============================================================================
procedure TForm1.FormCreate(Sender: TObject);
var
WindowStyle : Longint;
begin
// 現在のスタイルの設定値を取得
WindowStyle := GetWindowLong(Form1.Handle, GWL_STYLE);
// スタイル値に応じてチェックボックスのチェック状態を変更
if (WindowStyle and WS_CAPTION) > 0 then CheckBox1.Checked := True;
if (WindowStyle and WS_BORDER) > 0 then CheckBox2.Checked := True;
if (WindowStyle and WS_THICKFRAME) > 0 then CheckBox3.Checked := True;
if (WindowStyle and WS_POPUP) > 0 then CheckBox4.Checked := True;
if (WindowStyle and WS_SYSMENU) > 0 then CheckBox5.Checked := True;
if (WindowStyle and WS_MAXIMIZEBOX) > 0 then CheckBox6.Checked := True;
if (WindowStyle and WS_MINIMIZEBOX) > 0 then CheckBox7.Checked := True;
// 全てのチェックボックスのOnClickは共通して使用
// 実際にはオブジェクト・インスペクタで設定した方が良
CheckBox1.OnClick := CheckBox1Click;
CheckBox2.OnClick := CheckBox1Click;
CheckBox3.OnClick := CheckBox1Click;
CheckBox4.OnClick := CheckBox1Click;
CheckBox5.OnClick := CheckBox1Click;
CheckBox6.OnClick := CheckBox1Click;
CheckBox7.OnClick := CheckBox1Click;
end ;
// =============================================================================
// チェックボックスのチェック状態によってフォームのスタイルを変更
// 最小化・最大化・閉じるボタンはキャプションバーが表示されていなければ表示さ
// ないことに注意
// =============================================================================
procedure TForm1.CheckBox1Click(Sender: TObject);
var
WindowStyle : Longint;
begin
// 現在のスタイルの設定値を取得
WindowStyle := GetWindowLong(Form1.Handle, GWL_STYLE);
// WS_CAPTIONたけを設定してもキャプションバー(タイトルバー)は表示されない
// WS_BORDERの値も必要
if (CheckBox1.Checked) then begin
WindowStyle := WindowStyle or WS_CAPTION;
end else begin
WindowStyle := WindowStyle and not WS_CAPTION;
end ;
if (CheckBox2.Checked) then begin
WindowStyle := WindowStyle or WS_BORDER;
end else begin
WindowStyle := WindowStyle and not WS_BORDER;
end ;
if (CheckBox3.Checked) then begin
WindowStyle := WindowStyle or WS_THICKFRAME;
end else begin
WindowStyle := WindowStyle and not WS_THICKFRAME
end ;
if (CheckBox4.Checked) then begin
WindowStyle := WindowStyle or WS_POPUP;
end else begin
WindowStyle := WindowStyle and not WS_POPUP;
end ;
if (CheckBox5.Checked) then begin
WindowStyle := WindowStyle or WS_SYSMENU;
end else begin
WindowStyle := WindowStyle and not WS_SYSMENU;
end ;
if (CheckBox6.Checked) then begin
WindowStyle := WindowStyle or WS_MAXIMIZEBOX;
end else begin
WindowStyle := WindowStyle and not WS_MAXIMIZEBOX;
end ;
if (CheckBox7.Checked) then begin
WindowStyle := WindowStyle or WS_MINIMIZEBOX;
end else begin
WindowStyle := WindowStyle and not WS_MINIMIZEBOX;
end ;
// スタイル変更を実行
SetWindowLong(Form1.Handle, GWL_STYLE, WindowStyle);
// 再描画のため必要(非クライアント領域を含めてウィンドウを再描画)
SetWindowPos(Handle, 0 , 0 , 0 , 0 , 0 ,
SWP_DRAWFRAME or
SWP_NOACTIVATE or
SWP_NOMOVE or
SWP_NOSIZE or
SWP_NOZORDER);
end ;
end .
通常のアプリケーションでは,いくつフォームを表示しても,タスクバーにはそのアプリケーションのメインフォームのアイコンのアイコンだけが表示されるようになっています.
以下のコードは,メインフォーム以外のフォームのアイコンをタスクバーに表示する例です.
図40
設計時画面
図41
別のフォームを表示
タスクバーに [Form1] [Form2] の両方のアイコンが表示される
図42
[Form2] の [Button1] クリックでアイコン変更
copy code
unit ShowTaskBarIconUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls;
type
TForm1 = class (TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form1: TForm1;
implementation
uses AnotherForm;
{$R *.dfm}
// =============================================================================
// Form2を表示
// =============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
Form2.Left := Self.Left - Self.Width - 20 ;
Form2.Top := Self.Top - Self.Height - 20 ;
Form2.Show;
end ;
// =============================================================================
// [終了]ボタン
// =============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
Close;
end ;
end .
copy code
unit AnotherForm;
interface
uses
{$IF CompilerVersion > 22.99}
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants,
System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs;
{$ELSE}
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
{$IFEND}
type
TForm2 = class (TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
protected
procedure CreateParams(var Params : TCreateParams); override ;
private
{ Private 宣言 }
public
{ Public 宣言 }
end ;
var
Form2: TForm2;
implementation
{$R *.dfm}
{ TForm2 }
// =============================================================================
// フォームのCreateParams処理
// =============================================================================
procedure TForm2.CreateParams(var Params: TCreateParams);
begin
inherited CreateParams(Params);
// このフォームの親はDesktopとする
// タスクバーにForm1とは別にアイコン表示される
Params.WndParent := GetDesktopWindow;
end ;
// =============================================================================
// このフォームのアイコンを変更
// 当然タスクバーのアイコンも変わる
// =============================================================================
procedure TForm2.Button1Click(Sender: TObject);
begin
Self.Icon.LoadFromFile('32_8bit.ico' );
SetClassLong(Handle, GCL_HICON, Self.Icon.Handle);
end ;
end .
13_ OnCreate イベントまたは OnShow イベントでフォームを閉じる
フォームの OnCreate イベントまたは OnShow イベント内で自フォームを閉じるメッセージをポストする例です.PostMessage で WM_CLOSE メッセージを自分自身にポストします.
イベントの実行が終了してから閉じるので,ほんのわずかですが一瞬だけフォームが表示されます.
図43
設計時の画面
リスト16
フォームの OnCreate イベント内で自フォームを閉じるメッセージをポスト
copy code
// =============================================================================
// このフォームを閉じる
// =============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
PostMessage(Handle, WM_CLOSE, 0 , 0 );
end ;
リスト17
フォームの OnShow イベント内でフォームを閉じるメッセージをポスト
copy code
// =============================================================================
// このフォームを閉じる
// =============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
PostMessage(Handle, WM_CLOSE, 0 , 0 );
end ;