Delphi Programming / Object Pascal

[掲載 2009年03月01日] [更新 2012年10月18日] Delphi サンプルプログラム集 |
| 152_WM_NCHITTEST とコントロールの移動リサイズ |
| 動作確認等 |
Windows XP(SP3) + Delphi XE Pro
Windows 7 U64(SP1) + Delphi XE Pro |
 |
152_MoveResize.zip [5,178 KB] 2012年09月15日版 (EXE 同梱) |
- 2009年10月31日
- [05_移動リサイズ可能な画像]を追加
- 2010年01月11日
- [05_移動リサイズ可能な画像]にチラツキ防止参考コードを追加
- 2012年09月14日
- チラつき防止コードを汎用的なコードに修正
- コントロールの移動リサイズ関係コンポーネントの紹介記事を追加
|
| WM_NCHITTEST によるコントロールの移動とリサイズ |
[100_OnDragOver イベントを使用したコントロールのドラッグ] から [150_オブジェクトリストの保存 2] までのサンプルではコントロールをマウスドラッグで移動しています。
コントロールの移動は、WM_NCHITTEST メッセージを処理することでも可能です。このメッセージは、キャプションバー (タイトルバー) の無いフォームをマウスドラッグで移動したり、キャプションバー以外の場所で移動する際にも利用することがあります。
ただし、この方法は TWinControl の派生コントロールだけで使用できます。TGraphicControl では利用できません。また、移動リサイズの操作中はマウスカーソルは変更できません。マウス関係のメッセージやイベントも捕捉できません。WM_NCHITTEST メッセージは全てのマウス関係メッセージの前に発生します。必要であれば、マウス関係のメッセージ処理は、この WM_NCHITTEST メッセージ内で行うことになります。
フォームに配置したコントロールのメッセージを処理するわけですが、フォームには複数のコントロールを配置するのが普通です。そのため、どのコントロールが発生したメッセージなのかを判定しなければなりません。しかし、対象のコントロールを配置したフォームに WM_NCHITTEST メッセージ処理のコードを追加しても、対象のコントロールでは、このメッセージは受信できません。
そこで、本ページのサンプルでは、移動またはリサイズ可能なコンポーネントを作成しています。つまり、対象のコントロール (コンポーネント) の派生クラス (継承クラス) を作成します。
そうすると、そのコンポーネントだけのメッセージの処理ができます。また、必要であれば、イベントも定義して、外部から操作することも可能になります。
ただし、本ページのサンプルでは、それらのコンポーネントは実行時に生成して使用しています。もちろん、パッケージに登録して利用することもできるようにしています。 |
| WM_NCHITTEST メッセージの戻り値が、次のように HICLIENT の時は、通常のマウスメッセージ処理となり、各種のマウスメッセージも、コントロールのマウスイベント処理も通常通り実行されるようになります。 |
procedure TplMoveResizeMemo.WmNCHitTest(var msg: TWmNCHitTest);
begin
inherited;
msg.Result := HTCLIENT;
end;
[備考 1]
フォームに配置したコントロールの WM_NCHITTEST メッセージは、TApplication.OnMessage では捕捉することはできません。該当コントロールのサブクラス化、あるいは、ローカルフックで捕らえることになります。この意味もあって、コンポーネントにしてテストしています。
|
[備考 2]
移動リサイズ関係のコンポーネント類を紹介していますが、これらは必ずしも WM_NCHITTEST メッセージを使用しているわけではありません。 |
コントロールの移動だけのサンプルです。
移動だけを可能した、TMemo を作成して使用します。
このテストプログラムでは、対象コントロールの上にマウスカーソルがくると、クリックしなくてもそのコントロールにフォーカスが移動します。 |
 |
図1
設計時画面
|
 |
図2
実行画面
- マウスが TMemo の上にくるとフォーカス移動
- マウスドラッグで移動する
- 移動の枠(グラブハンドル)等の表示はない
|
リスト1
WM_NCHITTEST メッセージを使用したマウスドラッグによる移動 |
unit MoveResize_TMemo1Unit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, plMoveMemo;
type
TForm1 = class(TForm)
Edit1: TEdit;
procedure FormShow(Sender: TObject);
private
{ Private 宣言 }
AMoveMemo : TplMoveMemo;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//=============================================================================
// コンポーネントの実行時生成
// マウスドラッグで移動とりサイズが可能
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
AMoveMemo := TplMoveMemo.Create(Self);
AMoveMemo.Parent := Self;
AMoveMemo.Left := 20;
AMoveMemo.Top := 50;
end;
end.
リスト2
マウスドラッグで移動可能な TMemo コンポーネントのソース |
unit plMoveMemo;
interface
uses
Windows, Messages, SysUtils, Classes, StdCtrls;
type
TplMoveMemo = class(TMemo)
private
{ Private 宣言 }
protected
procedure WmNCHitTest(var msg: TWmNCHitTest); message WM_NCHITTEST;
procedure WmSysCommand(var msg: TWmSysCommand); message WM_SYSCOMMAND;
public
Constructor Create(AOWner: TComponent); override;
destructor Destroy; override;
end;
procedure Register;
implementation
//=============================================================================
// コンポーネントをコンポーネントパレットに登録する手続き
//=============================================================================
procedure Register;
begin
RegisterComponents('plXRAY', [TplMoveMemo]);
end;
{ TMoveMemo }
//=============================================================================
// Create処理
// inheritedだけなので必要ないが、後の処理の追加のために実装しておく
//=============================================================================
constructor TplMoveMemo.Create(AOWner: TComponent);
begin
inherited Create(AOwner);
//初期化処理のコード
end;
//=============================================================================
// Destroy処理
// inheritedだけなので必要ないが、後の処理の追加のために実装しておく
//=============================================================================
destructor TplMoveMemo.Destroy;
begin
//終了処理のコード
inherited Destroy;
end;
//=============================================================================
// WM_NCHITTESTは[現在マウスカーソルがある部分は何か]を決定する時に送られるメ
// ッセージ
// このメッセージの戻値(msg.Result)を変更することで動作を変える
// HTCAPTION キャプションを掴んだと思わせる
// つまりドラッグで移動可能
// HTLEFT フォームの左端にマウスがあると思わせる
// つまりマウスドラッグで横幅のサイズ変更が可能
//
// WM_NCHITTESTをコントロールで実装した場合は、WM_LBUTTONDOWN等のメッセージは
// 無効となる。またカーソルの形状も強制的に変更となるため、操作中のカーソル形
// 状は変更できない(システムのカーソルを変更すれば可能かも知れない)
//=============================================================================
procedure TplMoveMemo.WmNCHitTest(var msg: TWmNCHitTest);
begin
//継承元のメッセージの実行
inherited;
//これ以降のコードを実行しなければ通常のTMemoと同じ動作となる
if not (csDesigning in ComponentState) then begin
//クライアント領域がクリックされた場合
if Msg.Result=htClient then begin
//フォーカス移動(文字入力が可能な状態に)してキャプションバーだと思わせる
SetFocus;
msg.Result := HTCAPTION;
end;
end;
end;
//=============================================================================
// WM_NCHITTESTメッセージの処理でコントロールのクライアント領域にマウスカーソ
// ルがある時にキャプションバー(HTCAPTION)としてしまったので、それに対する処置
// この処理がないとダブルクリックでAlign:=alClient;としたのと同じとなる
//=============================================================================
procedure TplMoveMemo.WmSysCommand(var msg: TWmSysCommand);
begin
//CmdTypeにSC_MAXIMIZE(最大化要求)が含まれている時は何もしない
if (msg.CmdType or SC_MAXIMIZE) = msg.CmdType then begin
end else begin
//通常通り
inherited;
end;
end;
end.
| 次は、移動とリサイズを可能にした TMemo コンポーネントです。マウス座標値を調査判定して、その位置により、WM_NCHITTEST の戻値を変更しています。 |
 |
図3
設計時画面
|
 |
図4
実行画面
- マウスが TMemo の上にくるとフォーカス移動
- マウスドラッグで移動
- クライアントの境界でリサイズ可能。カーソルは位置に応じて自動的に変わる
|
リスト3
WM_NCHITTEST メッセージを使用したコントロールの移動とリサイズ |
unit MoveResize_TMemo2Unit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, plMoveResizeMemo;
type
TForm1 = class(TForm)
Edit1: TEdit;
procedure FormShow(Sender: TObject);
private
{ Private 宣言 }
AMoveResizeMemo : TplMoveResizeMemo;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//=============================================================================
// コンポーネントの実行時生成
// マウスドラッグで移動とりサイズが可能
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
AMoveResizeMemo := TplMoveResizeMemo.Create(Self);
AMoveResizeMemo.Parent := Self;
AMoveResizeMemo.Left := 20;
AMoveResizeMemo.Top := 50;
end;
end.
リスト4
マウスドラッグで移動リサイズ可能な TMemo コンポーネントのソース |
unit plMoveResizeMemo;
interface
uses
Windows, Messages, SysUtils, Classes, StdCtrls;
type
TplMoveResizeMemo = class(TMemo)
private
{ Private 宣言 }
protected
procedure WmNCHitTest(var msg: TWmNCHitTest); message WM_NCHITTEST;
procedure WmSysCommand(var msg: TWmSysCommand); message WM_SYSCOMMAND;
public
Constructor Create(AOWner: TComponent); override;
destructor Destroy; override;
end;
procedure Register;
implementation
//=============================================================================
// コンポーネントをコンポーネントパレットに登録する手続き
//=============================================================================
procedure Register;
begin
RegisterComponents('plXRAY', [TplMoveResizeMemo]);
end;
{ TMoveResizeMemo }
//=============================================================================
// Create処理
// inheritedだけなので必要ないが、後の処理の追加のために実装しておく
//=============================================================================
constructor TplMoveResizeMemo.Create(AOWner: TComponent);
begin
inherited Create(AOwner);
//初期化処理のコード
end;
//=============================================================================
// Destroy処理
// inheritedだけなので必要ないが、後の処理の追加のために実装しておく
//=============================================================================
destructor TplMoveResizeMemo.Destroy;
begin
//終了処理のコード
inherited Destroy;
end;
//=============================================================================
// WM_NCHITTESTは[現在マウスカーソルがある部分は何か]を決定する時に送られるメ
// ッセージ
// このメッセージの戻値(msg.Result)を変更することで動作を変える
// HTCAPTION キャプションを掴んだと思わせる
// つまりドラッグで移動可能
// HTLEFT フォームの左端にマウスがあると思わせる
// つまりマウスドラッグで横幅のサイズ変更が可能
//
// ここではクライアント領域の境の5ドット分内にマウスが来た時にその境界とする
// まずマウス位置の上下左右を変数に代入し、その組合せで判定する
//
// WM_NCHITTESTをコントロールで実装した場合は、WM_LBUTTONDOWN等のメッセージは
// 無効となる。またカーソルの形状も強制的に変更となるため、操作中のカーソル形
// 状は変更できない(システムのカーソルを変更すれば可能かも知れない)
//=============================================================================
procedure TplMoveResizeMemo.WmNCHitTest(var msg: TWmNCHitTest);
const
mptLeft = $01;
mptTop = $02;
mptRight = $04;
mptBottom = $08;
var
APos : TPoint;
PosFlag : Byte;
begin
//継承元のメッセージの実行
inherited;
//これ以降のコードを実行しなければ通常のTMemoと同じ動作となる
if (csDesigning in ComponentState) then exit;
//クライアント領域でのマウス位置を取得
APos := ScreenToClient(Point(msg.XPos, msg.YPos));
PosFlag := $00;
//左端から5ドット未満
if (APos.X < 5) then PosFlag := PosFlag or mptLeft;
//上端から5ドット未満
if (APos.Y < 5) then PosFlag := PosFlag or mptTop;
//右端から5ドット未満
if (ClientWidth - APos.X) < 5 then PosFlag := PosFlag or mptRight;
//下端から5ドット未満
if (ClientHeight - APos.Y) < 5 then PosFlag := PosFlag or mptBottom;
case PosFlag of
mptLeft : msg.Result := HTLEFT;
mptTop : msg.Result := HTTOP;
mptTop+mptLeft : msg.Result := HTTOPLEFT;
mptRight : msg.Result := HTRIGHT;
mptTop+mptRight : msg.Result := HTTOPRIGHT;
mptBottom : msg.Result := HTBOTTOM;
mptBottom+mptLeft : msg.Result := HTBOTTOMLEFT;
mptBottom+mptRight : msg.Result := HTBOTTOMRIGHT;
else
SetFocus;
msg.Result := HTCAPTION;
end;
end;
//=============================================================================
// WM_NCHITTESTメッセージの処理でコントロールのクライアント領域にマウスカーソ
// ルがある時にキャプションバー(HTCAPTION)としてしまったので、それに対する処置
// この処理がないとダブルクリックでAlign:=alClient;としたのと同じとなる
//=============================================================================
procedure TplMoveResizeMemo.WmSysCommand(var msg: TWmSysCommand);
begin
//CmdTypeにSC_MAXIMIZE(最大化要求)が含まれている時は何もしない
if (msg.CmdType or SC_MAXIMIZE) = msg.CmdType then begin
end else begin
//通常通り
inherited;
end;
end;
end.
| TCustomPanel を継承したコンポーネントも作成してテストしてみました。 |
 |
図5
実行画面 |
リスト5
移動リサイズ可能な TPanel のテスト |
unit MoveResize_TPanelUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, plMoveResizePanel;
type
TForm1 = class(TForm)
procedure FormShow(Sender: TObject);
private
{ Private 宣言 }
AMoveResizePanel : TplMoveResizePanel;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//=============================================================================
// コンポーネントの実行時生成
// マウスドラッグで移動とりサイズが可能
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
AMoveResizePanel := TplMoveResizePanel.Create(Self);
AMoveResizePanel.Parent := Self;
AMoveResizePanel.Left := 20;
AMoveResizePanel.Top := 50;
AMoveResizePanel.Color := clYellow;
//このコードがないとColorプロパティが反映されない(テーマが有効な場合)
AMoveResizePanel.ParentBackground := False;
end;
end.
リスト6
マウスドラッグで移動可能な TPanel コンポーネントのソース |
unit plMoveResizePanel;
interface
uses
Windows, Messages, SysUtils, Classes, StdCtrls, ExtCtrls;
type
TplMoveResizePanel = class(TCustomPanel)
private
{ Private 宣言 }
protected
procedure WmNCHitTest(var msg: TWmNCHitTest); message WM_NCHITTEST;
procedure WmSysCommand(var msg: TWmSysCommand); message WM_SYSCOMMAND;
public
constructor Create(AOWner: TComponent); override;
destructor Destroy; override;
published
property Color;
end;
procedure Register;
implementation
//=============================================================================
// コンポーネントをコンポーネントパレットに登録する手続き
//=============================================================================
procedure Register;
begin
RegisterComponents('plXRAY', [TplMoveResizePanel]);
end;
{ TMoveResizePanel }
//=============================================================================
// Create処理
//=============================================================================
constructor TplMoveResizePanel.Create(AOWner: TComponent);
begin
inherited Create(AOwner);
end;
//=============================================================================
// Destroy処理
//=============================================================================
destructor TplMoveResizePanel.Destroy;
begin
inherited Destroy;
end;
//=============================================================================
// WM_NCHITTESTは[現在マウスカーソルがある部分は何か]を決定する時に送られるメ
// ッセージ
// このメッセージの戻値(msg.Result)を変更することで動作を変える
// HTCAPTION キャプションを掴んだと思わせる
// つまりドラッグで移動可能
// HTLEFT フォームの左端にマウスがあると思わせる
// つまりマウスドラッグで横幅のサイズ変更が可能
//
// ここではクライアント領域の境の5ドット分内にマウスが来た時にその境界とする
// まずマウス位置の上下左右を変数に代入し、その組合せで判定する
//
// WM_NCHITTESTをコントロールで実装した場合は、WM_LBUTTONDOWN等のメッセージは
// 無効となる。またカーソルの形状も強制的に変更となるため、操作中のカーソル形
// 状は変更できない(システムのカーソルを変更すれば可能かも知れない)
//=============================================================================
procedure TplMoveResizePanel.WmNCHitTest(var msg: TWmNCHitTest);
const
mptLeft = $01;
mptTop = $02;
mptRight = $04;
mptBottom = $08;
var
APos : TPoint;
PosFlag : Byte;
begin
//継承元のメッセージの実行
inherited;
//これ以降のコードを実行しなければ通常のTPanelと同じ動作となる
if (csDesigning in ComponentState) then exit;
//クライアント領域でのマウス位置を取得
APos := ScreenToClient(Point(msg.XPos, msg.YPos));
PosFlag := $00;
//左端から5ドット未満
if (APos.X < 5) then PosFlag := PosFlag or mptLeft;
//上端から5ドット未満
if (APos.Y < 5) then PosFlag := PosFlag or mptTop;
//右端から5ドット未満
if (ClientWidth - APos.X) < 5 then PosFlag := PosFlag or mptRight;
//下端から5ドット未満
if (ClientHeight - APos.Y) < 5 then PosFlag := PosFlag or mptBottom;
case PosFlag of
mptLeft : msg.Result := HTLEFT;
mptTop : msg.Result := HTTOP;
mptTop+mptLeft : msg.Result := HTTOPLEFT;
mptRight : msg.Result := HTRIGHT;
mptTop+mptRight : msg.Result := HTTOPRIGHT;
mptBottom : msg.Result := HTBOTTOM;
mptBottom+mptLeft : msg.Result := HTBOTTOMLEFT;
mptBottom+mptRight : msg.Result := HTBOTTOMRIGHT;
else
msg.Result := HTCAPTION;
end;
end;
//=============================================================================
// WM_NCHITTESTメッセージの処理でコントロールのクライアント領域にマウスカーソ
// ルがある時にキャプションバー(HTCAPTION)としてしまったので、それに対する処置
// この処理がないとダブルクリックでAlign:=alClient;としたのと同じとなる
//=============================================================================
procedure TplMoveResizePanel.WmSysCommand(var msg: TWmSysCommand);
begin
//CmdTypeにSC_MAXIMIZE(最大化要求)が含まれている時は何もしない
if (msg.CmdType or SC_MAXIMIZE) = msg.CmdType then begin
end else begin
//通常通り
inherited;
end;
end;
end.
| 04_TImage は WM_NCHITTEST メッセージ では移動リサイズ不可 |
| TImage やTShape は TGraphicControl の派生コンポーネントです。したがって WM_NCHITTEST メッセージで移動リサイズの処理はできません。このことを確認するためのテストプログラムです。 |
 |
図6
実行画面
|
リスト7
TImage は WM_NCHITTEST メッセージ で移動リサイズできないことを確認するテスト |
unit MoveResize_TImageUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, plMoveResizeImage;
type
TForm1 = class(TForm)
procedure FormShow(Sender: TObject);
private
{ Private 宣言 }
AMoveResizeImage : TplMoveResizeImage;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//=============================================================================
// コンポーネントの実行時生成
// マウスドラッグで移動とりサイズをテスト
// 実際にはできない
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
AMoveResizeImage := TplMoveResizeImage.Create(Self);
AMoveResizeImage.Parent := Self;
AMoveResizeImage.Left := 20;
AMoveResizeImage.Top := 30;
AMoveResizeImage.Picture.LoadFromFile('TestImage.bmp');
AMoveResizeImage.Stretch := True;
end;
end.
リスト8
WM_NCHITTESTメッセージのコードを追加したTImageの継承コンポーネントのソース
実際には動作しない |
unit plMoveResizeImage;
interface
uses
Windows, Messages, SysUtils, Classes, StdCtrls, Graphics, ExtCtrls;
type
TplMoveResizeImage = class(TImage)
private
{ Private 宣言 }
protected
procedure WmNCHitTest(var msg: TWmNCHitTest); message WM_NCHITTEST;
procedure WmSysCommand(var msg: TWmSysCommand); message WM_SYSCOMMAND;
public
constructor Create(AOWner: TComponent); override;
destructor Destroy; override;
end;
procedure Register;
implementation
//=============================================================================
// コンポーネントをコンポーネントパレットに登録する手続き
//=============================================================================
procedure Register;
begin
RegisterComponents('plXRAY', [TplMoveResizeImage]);
end;
{ TMoveResizeImage }
//=============================================================================
// Create処理
// inheritedだけなので必要ないが、後での処理の追加のために実装しておく
//=============================================================================
constructor TplMoveResizeImage.Create(AOWner: TComponent);
begin
inherited Create(AOwner);
end;
//=============================================================================
// Destroy処理
// inheritedだけなので必要ないが、後での処理の追加のために実装しておく
//=============================================================================
destructor TplMoveResizeImage.Destroy;
begin
inherited Destroy;
end;
//=============================================================================
// WM_NCHITTESTは[現在マウスカーソルがある部分は何か]を決定する時に送られるメ
// ッセージ
// このメッセージの戻値(msg.Result)を変更することで動作を変える
// HTCAPTION キャプションを掴んだと思わせる
// つまりドラッグで移動可能
// HTLEFT フォームの左端にマウスがあると思わせる
// つまりマウスドラッグで横幅のサイズ変更が可能
//
// ここではクライアント領域の境の5ドット分内にマウスが来た時にその境界とする
// まずマウス位置の上下左右を変数に代入し、その組合せで判定する
//
// WM_NCHITTESTをコントロールで実装した場合は、WM_LBUTTONDOWN等のメッセージは
// 無効となる。またカーソルの形状も強制的に変更となるため、ドラッグ中のカーソ
// ル形状は変更できない(システムのカーソルを変更すれば可能かも知れない)
//=============================================================================
procedure TplMoveResizeImage.WmNCHitTest(var msg: TWmNCHitTest);
const
mptLeft = $01;
mptTop = $02;
mptRight = $04;
mptBottom = $08;
var
APos : TPoint;
PosFlag : Byte;
begin
//継承元のメッセージの実行
inherited;
//これ以降のコードを実行しなければ通常のTPanelと同じ動作となる
if (csDesigning in ComponentState) then exit;
//クライアント領域でのマウス位置を取得
APos := ScreenToClient(Point(msg.XPos, msg.YPos));
PosFlag := $00;
//左端から5ドット未満
if (APos.X < 5) then PosFlag := PosFlag or mptLeft;
//上端から5ドット未満
if (APos.Y < 5) then PosFlag := PosFlag or mptTop;
//右端から5ドット未満
if (ClientWidth - APos.X) < 5 then PosFlag := PosFlag or mptRight;
//下端から5ドット未満
if (ClientHeight - APos.Y) < 5 then PosFlag := PosFlag or mptBottom;
case PosFlag of
mptLeft : msg.Result := HTLEFT;
mptTop : msg.Result := HTTOP;
mptTop+mptLeft : msg.Result := HTTOPLEFT;
mptRight : msg.Result := HTRIGHT;
mptTop+mptRight : msg.Result := HTTOPRIGHT;
mptBottom : msg.Result := HTBOTTOM;
mptBottom+mptLeft : msg.Result := HTBOTTOMLEFT;
mptBottom+mptRight : msg.Result := HTBOTTOMRIGHT;
else
msg.Result := HTCAPTION;
end;
end;
//=============================================================================
// WM_NCHITTESTメッセージの処理でコントロールのクライアント領域にマウスカーソ
// ルがある時にキャプションバー(HTCAPTION)としてしまったので、それに対する処置
// この処理がないとダブルクリックでAlign:=alClient;としたのと同じとなる
//=============================================================================
procedure TplMoveResizeImage.WmSysCommand(var msg: TWmSysCommand);
begin
//CmdTypeにSC_MAXIMIZE(最大化要求)が含まれている時は何もしない
if (msg.CmdType or SC_MAXIMIZE) = msg.CmdType then begin
end else begin
//通常通り
inherited;
end;
end;
end.
TPanel は他のコンポーネントのコンテナとなるコントロールです。ここに TImage を乗せ、TImage に画像や図形を表示すれば、見かけ上画像や図形が移動リサイズできているよう見えます。
TImage には、画像操作のためのいろいろいなメソッドやプロパティが実装されています。Timage プロパティを持つコンポーネントはこの点では便利です。
TPanel は、private 部に Canvas プロパティがあります。Paint メソッドを override したメソッド内に画像描画のコードを追加して、画像表示機能付きの TPanel とすることもできます。
[画像表示機能付き縦横両用ラベルコンポーネント]では、この方法で TLabel に画像を表示するようにしています。
また、[160] [08-01_TPanel の継承クラスの Paint メソッドで画像を描画] では、TPanel を実行時に生成して画像を表示しています。 |
 |
図7
実行画面
|
リスト9
WM_NCHITTEST メッセージを使用した、移動リサイズ可能な TImage コンポのテスト |
unit MoveResize_TPanelUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, plMoveResizePanelEx;
type
TForm1 = class(TForm)
procedure FormShow(Sender: TObject);
private
{ Private 宣言 }
AMoveResizePanel : TplMoveResizePanelEx;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//=============================================================================
// コンポーネントの実行時生成
// マウスドラッグで移動とりサイズが可能
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
AMoveResizePanel := TplMoveResizePanelEx.Create(Self);
with AMoveResizePanel do begin
Parent := Self;
Left := 20;
Top := 50;
Width := 130;
Height := 130;
DoubleBuffered := True;
end;
with AMoveResizePanel.Image do begin
Picture.LoadFromFile('TestImage.bmp');
Stretch := True;
end;
end;
end.
リスト10
コンポーネントのソース
移動リサイズ可能な TImageコンポ。実際には TPanel を移動リサイズする
移動リサイズの可否のプロパティを実装した方がいいかもしれない |
unit plMoveResizePanelEx;
interface
uses
Windows, Messages, SysUtils, Classes, StdCtrls, ExtCtrls, Controls;
type
TplMoveResizePanelEx = class(TCustomPanel)
private
{ Private 宣言 }
FImage : TImage;
procedure SetFImage(const Value: TImage);
protected
procedure SetParent(AParent: TWinControl); override;
procedure WmNCHitTest(var msg: TWmNCHitTest); message WM_NCHITTEST;
procedure WmSysCommand(var msg: TWmSysCommand); message WM_SYSCOMMAND;
procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
public
constructor Create(AOWner: TComponent); override;
destructor Destroy; override;
property Image : TImage read FImage write SetFImage;
published
property Color;
end;
procedure Register;
implementation
//=============================================================================
// コンポーネントをコンポーネントパレットに登録する手続き
//=============================================================================
procedure Register;
begin
RegisterComponents('plXRAY', [TplMoveResizePanelEx]);
end;
{ TMoveResizePanelEx }
//=============================================================================
// Create処理
//=============================================================================
constructor TplMoveResizePanelEx.Create(AOWner: TComponent);
begin
inherited Create(AOwner);
end;
//=============================================================================
// Destroy処理
//=============================================================================
destructor TplMoveResizePanelEx.Destroy;
begin
inherited Destroy;
end;
//=============================================================================
// ParentがSetされたらTImageを配置
//=============================================================================
procedure TplMoveResizePanelEx.SetParent(AParent: TWinControl);
begin
inherited;
if Parent = nil then exit;
//既に存在すれば以下は不要
//SetParentは破棄の時など何回も呼ばれるので条件判断が必要
if FImage=nil then begin
FImage := TImage.Create(Self);
FImage.Parent := Self;
FImage.Align := alClient;
end;
end;
//=============================================================================
// オブジェクトのプロパティの書込みはメソッドを実装してAssignする
//=============================================================================
procedure TplMoveResizePanelEx.SetFImage(const Value: TImage);
begin
FImage.Assign(Value);
end;
//=============================================================================
// WM_NCHITTESTは[現在マウスカーソルがある部分は何か]を決定する時に送られるメ
// ッセージ
// このメッセージの戻値(msg.Result)を変更することで動作を変える
// HTCAPTION キャプションを掴んだと思わせる
// つまりドラッグで移動可能
// HTLEFT フォームの左端にマウスがあると思わせる
// つまりマウスドラッグで横幅のサイズ変更が可能
//
// ここではクライアント領域の境の5ドット分内にマウスが来た時にその境界とする
// まずマウス位置の上下左右を変数に代入し、その組合せで判定する
//
// WM_NCHITTESTをコントロールで実装した場合は、WM_LBUTTONDOWN等のメッセージは
// 無効となる。またカーソルの形状も強制的に変更となるため、操作中のカーソル形
// 状は変更できない(システムのカーソルを変更すれば可能かも知れない)
//=============================================================================
procedure TplMoveResizePanelEx.WmNCHitTest(var msg: TWmNCHitTest);
const
mptLeft = $01;
mptTop = $02;
mptRight = $04;
mptBottom = $08;
var
APos : TPoint;
PosFlag : Byte;
begin
//継承元のメッセージを実行
inherited;
//これ以降のコードを実行しなければ通常のTPanelと同じ動作となる
if (csDesigning in ComponentState) then exit;
//クライアント領域でのマウス位置を取得
APos := ScreenToClient(Point(msg.XPos, msg.YPos));
PosFlag := $00;
//左端から5ドット未満
if (APos.X < 5) then PosFlag := PosFlag or mptLeft;
//上端から5ドット未満
if (APos.Y < 5) then PosFlag := PosFlag or mptTop;
//右端から5ドット未満
if (ClientWidth - APos.X) < 5 then PosFlag := PosFlag or mptRight;
//下端から5ドット未満
if (ClientHeight - APos.Y) < 5 then PosFlag := PosFlag or mptBottom;
case PosFlag of
mptLeft : msg.Result := HTLEFT;
mptTop : msg.Result := HTTOP;
mptTop+mptLeft : msg.Result := HTTOPLEFT;
mptRight : msg.Result := HTRIGHT;
mptTop+mptRight : msg.Result := HTTOPRIGHT;
mptBottom : msg.Result := HTBOTTOM;
mptBottom+mptLeft : msg.Result := HTBOTTOMLEFT;
mptBottom+mptRight : msg.Result := HTBOTTOMRIGHT;
else
msg.Result := HTCAPTION;
end;
end;
//=============================================================================
// WM_NCHITTESTメッセージの処理でコントロールのクライアント領域にマウスカーソ
// ルがある時にキャプションバー(HTCAPTION)としてしまったので、それに対する処置
// この処理がないとダブルクリックでAlign:=alClient;としたのと同じとなる
//=============================================================================
procedure TplMoveResizePanelEx.WmSysCommand(var msg: TWmSysCommand);
begin
//CmdTypeにSC_MAXIMIZE(最大化要求)が含まれている時は何もしない
if (msg.CmdType or SC_MAXIMIZE) = msg.CmdType then begin
end else begin
//通常通り
inherited;
end;
end;
//=============================================================================
// WM_ERASEBKGNDメッセージ処理
// 何も描画しない
// 本サンプルでは、TPanel上にに配置したTImage に画像をalClientで描画しているの
// で、TPanelの描画は不要
// 画像に透明部分がある時に、TImage.Transparent := True;にすると正常に描画され
// ない。別の処理が必要となる。別の処理は、添付のサンプルプログラムを参照
//=============================================================================
procedure TplMoveResizePanelEx.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
//
end;
end.
| 06_TPanel の描画チラつきを抑制する汎用的なコード |
| 上のサンプルプログラムでは、TPanel の DoubleBuffered プロパティを True にしてもチラつきを防止することはできません。そこで、次のように WM_ERASEBKGND メッセージでを実装し、バックグラウンドの描画をしないようにしています。 |
procedure TplMoveResizePanelEx.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
//
end;
| また、WM_ERASEBKGND メッセージのかわりに、コンポーネントのスタイルに csOpaque を含めることでもチラつきを防止できます。 |
constructor TplPicturePanel.Create(AOWner: TComponent);
begin
inherited Create(AOwner);
ControlStyle := ControlStyle + [csOpaque];
DoubleBuffered := True;
end;
上のサンプルではこのコードでチラつきを防止できますが、TImage の Transparent を True にすると、ビットマップの透過色の部分が描画されず、黒くなってしまいます。
一般に、TPanel の上に他のコントロールを配置した場合、TPanel を移動リサイズすると、チラつきが発生します。このチラつきは、DoubleBuffered や WM_ERASEBKGND メッセージの処理だけでは抑えることができないことがあります。河邦 正さんが作成した「TImage 等のちらつきを効率良く抑制する」というサンプルコードがあります。このコードを使用するとこの問題を回避できます。
このコードを使用したサンプルも添付しておきました。
このコードは、TPanel にコントロールを配置して使用する場合に有効です。この後の [画像表示機能付き TPanel] のように、TPanel の内部で描画コードを実行するような場合には、このままでは正常に動作しません。 |
リスト11
チラツキを抑制するための汎用的な処理コード |
//=============================================================================
// WM_PAINTメッセージ処理
// ちらつき防止用
// 旧Nifty FDelphi 16番会議室「玉石混淆みんなで作るSample蔵」の
// 河邦 正 さんのまコードを借用
//=============================================================================
procedure TplMoveResizePanelEx.WMPaint(var Message: TWMPaint);
var
PStruct : TPaintStruct;
AWidth : Integer;
AHeigth : Integer;
DC : HDC;
bmp : HBITMAP;
bmpOld : HBITMAP;
i : Integer;
Count : Integer;
SaveDCIndex : Integer;
begin
if Message.DC <> 0 then begin
inherited;
end else begin
BeginPaint(Handle, PStruct);
try
AWidth := PStruct.rcPaint.Right - PStruct.rcPaint.Left;
AHeigth := PStruct.rcPaint.Bottom - PStruct.rcPaint.Top;
DC := GetDC(HWND(0));
bmp := CreateCompatibleBitmap(DC, AWidth, AHeigth);
ReleaseDC(HWND(0), DC);
try
Message.DC := CreateCompatibleDC(HDC(0));
try
bmpOld := SelectObject(Message.DC, bmp);
SetWindowOrgEx(Message.DC, PStruct.rcPaint.Left, PStruct.rcPaint.Top, nil);
FillRect(Message.DC, PStruct.rcPaint, Brush.Handle);
Count := ControlCount;
for i := 0 to Count - 1 do begin
if Controls[i] is TWinControl then break;
if Controls[i].Visible and
RectVisible(PStruct.hdc, Controls[i].BoundsRect) then begin
SaveDCIndex := SaveDC(Message.DC);
OffsetWindowOrgEx (Message.DC,
-Controls[i].Left,
-Controls[i].Top,
nil);
IntersectClipRect (Message.DC,
0,
0,
Controls[i].Width,
Controls[i].Height);
Controls[i].Perform(WM_PAINT, Message.DC, 0);
RestoreDC(Message.DC, SaveDCIndex);
end;
end;
BitBlt(PStruct.hdc,
PStruct.rcPaint.Left,
PStruct.rcPaint.Top,
AWidth,
AHeigth,
Message.DC,
PStruct.rcPaint.Left,
PStruct.rcPaint.Top,
SRCCOPY);
SelectObject(Message.DC, bmpOld);
finally
DeleteDC(Message.DC);
Message.DC := 0;
end;
finally
DeleteObject(bmp);
end;
finally
EndPaint(Handle, PStruct);
end;
end;
end;
//=============================================================================
// WM_ERASEBKGNDメッセージ処理
// このメッセージで何も処理しなければちらつかないが、このコントロール上の描画
// が正常に実行されない
// そこで背景を該当コントロールのBrushハンドルで塗り潰す
// 本サンプルでは、何もない空のコードでもよい
//=============================================================================
procedure TplMoveResizePanelEx.WMEraseBkgnd(var Message: TWMEraseBkgnd);
var
Count : Integer;
i : Integer;
begin
Count := ControlCount - 1;
for i := 0 to Count do begin
if Controls[i] is TWinControl then begin
if Controls[i].Visible then begin
FillRect(Message.DC,
TWinControl(Controls[i]).BoundsRect,
TWinControl(Controls[i]).Brush.Handle);
end;
end;
end;
end;
TPanel は、private 部に Canvas プロパティがあります。そこで、Paint メソッドを override したメソッド内に画像描画のコードを追加して、画像表示機能付きの TPanel を作成してみました。
TPanel は透過しないので、背景に模様を描いたり、枠を表示する等、ある意味便利です。写真等の画像表示には向いているかも知れません。
[画像表示機能付き縦横両用ラベルコンポーネント] も、この方法で TLabel に画像を表示するようにしています。TLabel は TGraphicControl の派生コンポーネントです。背景を透過できますが、他のコントロール、TWinControl の前面には表示できません。
このままでは、リサイズの際に「マウスで掴む」位置の判定が困難です。実用的にするには、ラバーバンドやグラブハンドルの表示が必要でしょう。
|
 |
図8
実行画面
- TPanel の Canvas に画像を描画
- 枠線も表示してみた
|
リスト12
画像表示機能付き TPanel
コンポーネントのテスト |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, plPicturePanel;
type
TForm1 = class(TForm)
procedure FormShow(Sender: TObject);
private
{ Private 宣言 }
PicturePanel : TplPicturePanel;
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//=============================================================================
// コンポーネントの実行時生成
// マウスドラッグで移動とりサイズが可能
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
Caption := '画像表示機能付き TPanel';
PicturePanel := TplPicturePanel.Create(Self);
PicturePanel.Parent := Self;
PicturePanel.Color := clWhite;
PicturePanel.SetBounds(50, 50, 150, 150);
PicturePanel.Picture.LoadFromFile('503.bmp');
PicturePanel.Picture.Bitmap.Transparent := True;
end;
end.
リスト13
画像表示機能付き TPanel コンポーネント |
unit plPicturePanel;
interface
uses
Windows, Messages, SysUtils, Classes, StdCtrls, ExtCtrls, Graphics, Forms,
Controls;
type
TplPicturePanel = class(TCustomPanel)
private
{ Private 宣言 }
FPicture: TPicture;
procedure SetPicture(const Value: TPicture);
protected
procedure WmNCHitTest(var msg: TWmNCHitTest); message WM_NCHITTEST;
procedure WmSysCommand(var msg: TWmSysCommand); message WM_SYSCOMMAND;
procedure Paint; override;
procedure WMEraseBkgnd(var Message: TWMEraseBkgnd); message WM_ERASEBKGND;
public
constructor Create(AOWner: TComponent); override;
destructor Destroy; override;
published
property Anchors;
property Align;
property BevelWidth;
property BevelOuter;
property BevelKind;
property BevelInner;
property BevelEdges;
property Color;
property ShowHint;
property PopupMenu;
property Visible;
property OnDragOver;
property OnEnter;
property OnExit;
property OnDragDrop;
property OnDblClick;
property OnMouseWheelUp;
property OnMouseWheelDown;
property OnMouseWheel;
property OnMouseUp;
property OnMouseLeave;
property OnMouseEnter;
property OnMouseDown;
property OnResize;
property Picture : TPicture read FPicture write SetPicture;
end;
procedure Register;
implementation
//=============================================================================
// コンポーネントをコンポーネントパレットに登録する手続き
//=============================================================================
procedure Register;
begin
RegisterComponents('plXRAY', [TplPicturePanel]);
end;
{ TplPicturePanel }
//=============================================================================
// Create処理
//=============================================================================
constructor TplPicturePanel.Create(AOWner: TComponent);
begin
inherited Create(AOwner);
Brush.Style := bsSolid;
BorderStyle := bsNone;
Color := clBtnFace;
FPicture := TPicture.Create;
//ちらつき防止。このコンポではWM_ERASEBKGNDと併用して防止
DoubleBuffered := True;
end;
//=============================================================================
// Destroy処理
//=============================================================================
destructor TplPicturePanel.Destroy;
begin
FPicture.Free;
inherited Destroy;
end;
//=============================================================================
// Pictureプロパティの設定用メソッド
//=============================================================================
procedure TplPicturePanel.SetPicture(const Value: TPicture);
begin
if Value <> nil then begin
FPicture.Assign(Value);
Repaint;
end;
end;
//=============================================================================
// Paintメソッド処理
// 描画が必要になると自動的に実行されるメソッド
// コンポの描画用のイベントを定義し、このPaintメソッド内でそのイベントのコード
// を実行するようにすれば、いろいろな描画コードに対応でき、汎用的になる
//=============================================================================
procedure TplPicturePanel.Paint;
var
ARect : TRect;
begin
inherited;
//背景を塗り潰す
Canvas.Brush.Color := Color;
Canvas.Brush.Style := bsSolid;
Canvas.FillRect(ClientRect);
if FPicture <> nil then begin
Canvas.StretchDraw(ClientRect, FPicture.Graphic);
Canvas.Font.Size := 13;
Canvas.Font.Color := clBlue;
SetBkMode(Canvas.Handle, TRANSPARENT);
Canvas.TextOut(10, 10, '画像表示機能付きパネル');
end;
//ビットマップのTransparentをTrueにすると、マウスで掴む位置の判定が困難と
//なるので、枠線を描画。つまりラバーバンド
Canvas.Brush.Color := clBlack;
Canvas.FrameRect(ClientRect);
end;
//=============================================================================
// WM_NCHITTESTは[現在マウスカーソルがある部分は何か]を決定する時に送られるメ
// ッセージ
// このメッセージの戻値(msg.Result)を変更することで動作を変える
// HTCAPTION キャプションを掴んだと思わせる
// つまりドラッグで移動可能
// HTLEFT フォームの左端にマウスがあると思わせる
// つまりマウスドラッグで横幅のサイズ変更が可能
//
// ここではクライアント領域の境の5ドット分内にマウスが来た時にその境界とする
// まずマウス位置の上下左右を変数に代入し、その組合せで判定する
//
// WM_NCHITTESTをコントロールで実装した場合は、WM_LBUTTONDOWN等のメッセージは
// 無効となる。またカーソルの形状も強制的に変更となるため、操作中のカーソル形
// 状は変更できない(システムのカーソルを変更すれば可能かも知れない)
//=============================================================================
procedure TplPicturePanel.WmNCHitTest(var msg: TWmNCHitTest);
const
mptLeft = $01;
mptTop = $02;
mptRight = $04;
mptBottom = $08;
var
APos : TPoint;
PosFlag : Byte;
begin
//継承元のメッセージの実行
inherited;
//これ以降のコードを実行しなければ通常のTPanelと同じ動作となる
if (csDesigning in ComponentState) then exit;
//クライアント領域でのマウス位置を取得
APos := ScreenToClient(Point(msg.XPos, msg.YPos));
PosFlag := $00;
//左端から5ドット未満
if (APos.X < 5) then PosFlag := PosFlag or mptLeft;
//上端から5ドット未満
if (APos.Y < 5) then PosFlag := PosFlag or mptTop;
//右端から5ドット未満
if (ClientWidth - APos.X) < 5 then PosFlag := PosFlag or mptRight;
//下端から5ドット未満
if (ClientHeight - APos.Y) < 5 then PosFlag := PosFlag or mptBottom;
case PosFlag of
mptLeft : msg.Result := HTLEFT;
mptTop : msg.Result := HTTOP;
mptTop+mptLeft : msg.Result := HTTOPLEFT;
mptRight : msg.Result := HTRIGHT;
mptTop+mptRight : msg.Result := HTTOPRIGHT;
mptBottom : msg.Result := HTBOTTOM;
mptBottom+mptLeft : msg.Result := HTBOTTOMLEFT;
mptBottom+mptRight : msg.Result := HTBOTTOMRIGHT;
else
msg.Result := HTCAPTION;
end;
end;
//=============================================================================
// WM_NCHITTESTメッセージの処理でコントロールのクライアント領域にマウスカーソ
// ルがある時にキャプションバー(HTCAPTION)としてしまったので、それに対する処置
// この処理がないとダブルクリックでAlign:=alClient;としたのと同じとなる
//=============================================================================
procedure TplPicturePanel.WmSysCommand(var msg: TWmSysCommand);
begin
//CmdTypeにSC_MAXIMIZE(最大化要求)が含まれている時は何もしない
if (msg.CmdType or SC_MAXIMIZE) = msg.CmdType then begin
end else begin
//通常通り
inherited;
end;
end;
//=============================================================================
// WM_ERASEBKGNDメッセージ処理
// 何も描画しない
// このコンポでは、描画は全てPaintメソッドで実行している
//=============================================================================
procedure TplPicturePanel.WMEraseBkgnd(var Message: TWMEraseBkgnd);
begin
//
end;
end.
コンポーネントの紹介です。
ネット上で探すと、コントロールの移動リサイズ用のコンポーネントを見つけることができます。その 1 つです。[TSizeCtrl - Version 7.2]というコンポーネントをテストしてみました。
以下のサイトにあります。SizeControl.pas をコンポーネントとして登録します。コンポーネントのアイコンリソースは SizeControl.res です。 |
 |
図9
TSizeCtrl コンポーネント |
 |
図10
添付のデモプログラム
- プロジェクトをコンパイルして実行
- 実行は Windows 7 U64 + Delphi XE
|
このコンポーネントは、
- TSizeCtrl をフォームに配置
- RegisterControl メソッドで移動リサイズするコントロールを指定
- UnRegisterControl メソッドで移動リサイズ対象からコントロールを除外できる
- Enabled プロパティを False にすると、全てのコントロールが対象から除外される
のように使用します。TControl の派生コントロール全てを操作できます。
以下はこのコンポーネントの簡単な使用例です。 |
 |
図11
設計時画面 |
 |
図12
実行画面
- マウスの左ボタンを押下すると、グラブハンドルが表示される
- [Shift] で複数のオブジェクトほを選択しての移動も可能
|
 |
図13
複数のコントロールの移動 |
リスト14
TSizeCtrl コンポーネントの簡単な使用例 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, SizeControl;
type
TForm1 = class(TForm)
SizeCtrl1: TSizeCtrl;
Panel1: TPanel;
Button1: TButton;
CheckBox1: TCheckBox;
Image1: TImage;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure CheckBox1Click(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//=============================================================================
// フォーム生成時の処理
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
Self.Caption := 'TSizeCtrl コンポの簡単な使用方法';
Self.DoubleBuffered := True;
CheckBox1.Checked := True;
//TSizeCtrlの対象コントロールの設定
SizeCtrl1.RegisterControl(Panel1);
SizeCtrl1.RegisterControl(Button1);
Image1.Stretch := True;
Image1.Transparent := False;
SizeCtrl1.RegisterControl(Image1);
end;
//=============================================================================
// フォーム破棄の時の処理
//=============================================================================
procedure TForm1.FormDestroy(Sender: TObject);
begin
//TSizeCtrlの対象コントロールから外す
SizeCtrl1.UnRegisterControl(Panel1);
SizeCtrl1.UnRegisterControl(Button1);
SizeCtrl1.UnRegisterControl(Image1);
SizeCtrl1.Enabled := False;
end;
//=============================================================================
// CheckBox1のOnClickイベント処理
// SizeCtrl1のEnableプロパティの切換え
//=============================================================================
procedure TForm1.CheckBox1Click(Sender: TObject);
begin
SizeCtrl1.Enabled := CheckBox1.Checked;
end;
//=============================================================================
// SizeCtrl1のEnableプロパティがFalseの時は動作しない
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowMessage('Button1 Clicked !');
end;
end.
| 下のリストは、このコンポーネントのデモプログラムに記述されているコードです。あるコントロールの子コントロール全てを対象にしたり、対象から除外することができます。 |
//-----------------------------------------------------------------------------
// 引数のTWinControl上に配置した全てのコントロールを、TSizeCtrlの操作対象に
// するためのプロシージャー(TSizeCtrlのRegisterControlメソッドの実行)
// TSizeCtrlのEnabledプロパティをFalseにすると、自動的に除外処理をするらしい
//
// 再帰処理している
//-----------------------------------------------------------------------------
procedure RegComponents(aParent: TWinControl; SizeCtrl: TSizeCtrl);
var
i: integer;
begin
for i := 0 to aParent.ControlCount -1 do
begin
//In this demo, Tag <> 0 prevents a control becoming a SizeCtrl target ...
if aParent.Controls[i].Tag = 0 then
SizeCtrl.RegisterControl(aParent.Controls[i]);
if aParent.Controls[i] is TWinControl then
RegComponents(TWinControl(aParent.Controls[i]), SizeCtrl);
end;
end;
//-----------------------------------------------------------------------------
// 引数のTWinControl上に配置した全てのコントロールを、TSizeCtrlの操作対象から
// 除外するプロシージャー(TSizeCtrlのUnRegisterControlメソッドの実行)
// TSizeCtrlのEnabledプロパティをFalseにすると、自動的に除外処理をするらしい
//
// 再帰処理している
//-----------------------------------------------------------------------------
procedure UnregComponents(aParent: TWinControl; SizeCtrl: TSizeCtrl);
var
i: integer;
begin
for i := 0 to aParent.ControlCount -1 do
begin
SizeCtrl.UnRegisterControl(aParent.Controls[i]);
if aParent.Controls[i] is TWinControl then
UnregComponents(TWinControl(aParent.Controls[i]), SizeCtrl);
end;
end;
これもコンポーネントの紹介です。
実行時デザインシステム (Runtime Design System) という名称のものです。以下の Torry's Delphi に登録されています。作者は、佐々木@六角堂 さんです。
DsnUnit.pas をコンポーネントとして登録します。アイコンのリソースは DsnUnit.dcr です。
実行時にコントロールのプロパティ等を設定する趣旨で開発されたようです。下図のように、オブジェクト・インスペクタのようなプロパティ設定ダイアログも表示できるようになっています。
|
 |
図14
コンポーネント群 |
 |
図15
デモプログラムの実行例
- [Sample1]フォルダのサンプルをコンパイルして実行
- [DsnSwitch]をクリックするとデザインモードになる
- コントロールをクリックすると、グラブハンドルが表示される
|
このコンポーネントの基本的な使用方法です。
- TDsnStage を配置。この上に移動リサイズするコントロールを配置する
- TDsn8Register を配置。DsnStageプロパティを TDsnStage にする
- TDsnSwitch を配置。DsnRegister プロパティを TDsnRegister にする
TDsnSwitch ボタンをクリックすると、TDsnStage の上に配置しているコントロール類がデザインモードとなり、マウス操作で移動リサイズ可能となります。この操作はトグル動作となっています。その他のコンポーネント類はヘルプで確認してください。日本語のヘルプが添付されています。
以下はこのコンポーネントの簡単な使用例です。
TDsnSwitch を使用しなくても、次のように、TDsnRegister の Designing プロパティの値でもデザインモードの切換えができます。
|
procedure TForm1.FormShow(Sender: TObject);
begin
DsnRegister1.Designing := True;
end;
 |
図16
設計時画面
- 左上のボタンが TDsnSwitch コンポ
- なくてもデザインモード切換は可能
|
 |
図17
実行画面
- [DsnSwitch1](左上のボタン)をクリックするとデザインモードになる
- [Shift] で複数のコントロールを選択しの移動も可能 (動作は重たい)
|
 |
図18
複数のコントロールを移動 |
リスト15
[実行時デザインシステム]コンポーネントの簡単な使用例 |
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, DsnUnit, Buttons, DsnSubMl, DsnSub8, DsnPanel, StdCtrls;
type
TForm1 = class(TForm)
DsnRegister1: TDsn8Register;
DsnSwitch1: TDsnSwitch;
DsnStage1: TDsnStage;
Panel1: TPanel;
Button1: TButton;
procedure FormCreate(Sender: TObject);
private
{ Private 宣言 }
public
{ Public 宣言 }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
//=============================================================================
// フォーム生成時の処理
//
// 以下を配置して、オブジェクト・インスペクタで設定
// DsnStage : TDsnStage
// この上に移動リサイズするコントロールを配置する
// Dsn8Register1 : TDsn8Register
// DsnStageプロパティをDsnStage1にする
// DsnSwitch1 : TDsnSwitch
// DsnRegisterプロパティをDsnRegister1にする
// 実行時にDsnSwitch1をクリックすると、デザインモードになる
// 再度クリックすると、デザインモードが終了する
// または、DsnRegister1.Designing := True; を実行する
//=============================================================================
procedure TForm1.FormCreate(Sender: TObject);
begin
Self.Caption := '実行時デザインシステム 簡単な使用例';
end;
end.
| 10_How to Move and Resize Controls at Run Time |
コンポーネントではありませんが、以下のリンクに、コントロールの実行移動リサイズのサンプルと解説のページがあります。3 ページあります。サンプルもダウンロードできます。
以下の図は、src110805 というファイルをダウンロードし、Windows 7 U64(SP1) + Delphi XE(UP1) でコンパイルして実行した結果です。 |
[備考]
2017年05月06日現在、上のリンクのページは記事の内容が変わっています。コードもダウンロードできません。
|
 |
図19
設計時画面 |
 |
図20
実行時画面
- [Allow moving and resizing] をチェック
- 任意のコントロールをクリック
- グラブハンドルが表示され移動リサイズ可能となる
|
| ダウンロードしたままのコードでは、グラブハンドルが上の図のようになっています。これを黒い色にするには、次のように、mover.pas 内の TMover.CreateNodes の部分に ParentBackground の値を設定します。 |
procedure TMover.CreateNodes;
var
Node: Integer;
Panel: TPanel;
begin
for Node := 0 to 7 do
begin
Panel := TPanel.Create(self);
with Panel do
begin
ParentBackground := False; //追加
BevelOuter := bvNone;
Color := clBlack;
Name := 'Node' + IntToStr(Node);
Width := 5;
Height := 5;
Parent := nil;
Visible := false;
FNodes.Add(Panel);
case Node of
0,4: Cursor := crSizeNWSE;
1,5: Cursor := crSizeNS;
2,6: Cursor := crSizeNESW;
3,7: Cursor := crSizeWE;
end;
OnMouseDown := NodeMouseDown;
OnMouseMove := NodeMouseMove;
OnMouseUp := NodeMouseUp;
end;
end;
end;
 |
図21
コードを修正した結果
|
|