VisibleUpdownプロパティ
TNumberEdit
に、VisibleUpdown
プロパティを追加したいと思います。VisibleUpdown
プロパティは、数値の増減をUpdown
ボタンで行うかどうかを指定するプロパティです。True
に設定すると、下図のように、編集ボックス内にUpdown
ボタンを表示します。
TInnerUpdownクラスの作成
Updown
ボタンはDelphiにTUpDown
ボタンがあるので、これを使用すると簡単です。しかし、TUpDown
ボタンは単独で使用可能な完成されたコントロールであるため、編集ボックス内で使うには問題があります。アプリケーションから変更してほしくないプロパティを変更されると、TNumberEdit
の動作に支障が生じる可能性があります。
そこで、TNumberEdit
用にカスタマイズできるよう、TCustomUpDown
クラスから派生して、TInnerUpDown
クラスを作成します。
準備
TCustomUpDownクラスは Vcl.ComCtrls ユニットで定義されています。このユニットを参照できるよう、NumberEdit ユニットの interface 部の uses に、Vcl.ComCtrls を追加します(7行目)。
unit NumberEdit;
interface
uses
System.SysUtils, System.Classes, Vcl.Controls, Vcl.StdCtrls, Winapi.Windows,
Winapi.Messages, Vcl.Clipbrd, Vcl.ComCtrls;
TInnerUpDownクラスの定義
TInnerUpDownクラスを定義します。TInnerUpDownクラスはTNumberEditの中で使用するので、TNumberEditより前に定義しなけれでなりません(10-15行目)。
type
// 内部用のUpDownボタン
TInnerUpDown = class(TCustomUpDown)
private
protected
public
end;
// NumberEditのベースクラス
TCustomNumberEdit = class(TCustomEdit)
private
VisibleUpdownプロパティの追加
TCustomNumberEdit
に VisibleUpdown
プロパティを追加します。VisibleUpdown
プロパティは protected
スコープにしてみます(37行目)。
protected
{ Protected 宣言 }
procedure Change; override;
function IsValidChar(const Key: Char): Boolean; virtual;
procedure KeyPress(var Key: Char); override;
function LimitValue: Extended; virtual;
procedure ShowValueText; virtual;
function TextToValue(const S: string): Extended; virtual;
// プロパティ
property VisibleUpdown: Boolean read GetVisibleUpdown write SetVisibleUpdown;
Getメソッドの作成
VisibleUpdown
プロパティから読み取る(read)値は、GetVisibleUpdown
メソッドの戻り値となります。今まで作成したプロパティは F~ というフィールドから読み取るようになっていましたが、VisibleUpdown
プロパティのようにメソッドの戻り値をプロパティに出力することもできます。
VisibleUpdownメソッドの宣言部分は、以下のようになります(24行目)。
TCustomNumberEdit = class(TCustomEdit)
private
{ Private 宣言 }
FComma: Boolean;
FDigits: Integer;
FValue: Extended;
function GetVisibleUpdown: Boolean;
プロパティの読み取り用メソッド(Getメソッド)は、戻り値が必要なため、関数(function
)になります。戻り値のデータ型は、プロパティの型と一致する必要があります。
GetVisibleUpdownメソッドの実装部は以下のようになります。
function TCustomNumberEdit.GetVisibleUpdown: Boolean;
begin
end;
メソッドの戻り値(イコール、プロパティの値)は、Updown
ボタンが表示されていれば True
、表示されていないかボタンが作成されていなければ False
になります。ボタンの状態を取得するには、ボタンにアクセスする手段が必要です。そこで、FUpdown
フィールドを宣言し、FUpdown
フィールドにUpdown
ボタンを参照させます。以下のようにFUpdown
フィールドを追加します(23行目)。
private
{ Private 宣言 }
FComma: Boolean;
FDigits: Integer;
FUpdown: TInnerUpdown;
FValue: Extended;
それでは、GetVisibleUpdownメソッドの中身を書きましょう。以下のようになります。
function TCustomNumberEdit.GetVisibleUpdown: Boolean;
begin
if FUpdown = nil then
Result := False
else
Result := FUpdown.Visible;
end;
Setメソッドの作成
SetVisibleUpdown
メソッドの宣言は以下のとおりです(29行目)。
procedure SetValue(const Value: Extended);
procedure SetVisibleUpdown(const Value: Boolean);
procedure WMPaste(var Message: TWMPaste); message WM_PASTE;
実装部は以下のようになります。
procedure TCustomNumberEdit.SetVisibleUpdown(const Value: Boolean);
begin
end;
さて、プロパティにTrue
が設定された場合から考えましょう。
Updown
ボタンが作成されていないときは、Updown
ボタンを作成し、画面に表示します。
Updown
ボタンが作成されているときは、そのボタンを表示します。
これをコードにすると、以下のようになります。
procedure TCustomNumberEdit.SetVisibleUpdown(const Value: Boolean);
begin
if Value then
begin
if FUpdown = nil then CreateUpDown;
FUpdown.Visible := True;
end;
end;
Updown
ボタンの作成は、念のため、別メソッドに分けることにしました。CreateUpDown
メソッドの中でUpdown
ボタンを作成します。
CreateUpDownメソッドの作成
CreateUpDown
メソッドの定義は以下のとおりです(25行目)。
private
{ Private 宣言 }
FComma: Boolean;
FDigits: Integer;
FUpdown: TInnerUpdown;
FValue: Extended;
procedure CreateUpDown;
CreateUpDown
メソッドの実装は以下のとおりです。
procedure TCustomNumberEdit.CreateUpDown;
begin
if FUpdown = nil then
begin
FUpdown := TInnerUpDown.Create(Self);
FUpdown.Parent := Self;
end;
end;
忘れていけないのは、Parent
の設定です。Parent
は表示する際の親コントロールになります。今回は編集ボックスの内側にUpdown
ボタンを表示するので、Updown
ボタンの親を編集ボックス(TCustomNumberEdit
コントール)にします。
ここで使用している Self
は、自分自身を指す特別な変数です。ここで言う自分自身とは、CreateUpDown
メソッドの持ち主、つまり、TCustomNumberEdit
を指しています(正確に書くと、Self
が指しているのは、今CreateUpDown
を実行している、TCustomNumberEdit
のオブジェクトインスタンスです)。
ボタンの位置とサイズの調整
Updown
ボタンを表示できるようになりましたが、これだけでは不十分です。ボタンの位置やサイズを指定していないので、変なところにUpdown
ボタンが表示されてしまいます。
ボタンの位置とサイズを調整するためのメソッドを作成しましょう。メソッドの名前は、AdjustUpDown
とします。定義は以下のとおりです(25行目)。
private
{ Private 宣言 }
FComma: Boolean;
FDigits: Integer;
FUpdown: TInnerUpdown;
FValue: Extended;
procedure AdjustUpDown;
procedure CreateUpDown;
実装は以下のとおりです。
procedure TCustomNumberEdit.AdjustUpDown;
var
BtnRect: TRect;
EditRect: TRect;
BorderWid: Integer;
begin
// コントロールのハンドルがないときは処理しない
if not HandleAllocated then Exit;
// ボーダーの幅を取得
if Ctl3D then
BorderWid := GetSystemMetrics(SM_CXEDGE)
else
BorderWid := GetSystemMetrics(SM_CXBORDER);
// ボタンの位置とサイズ
if FUpdown = nil then
begin
BtnRect.Left := Width - (Width - ClientWidth) div 2 - BorderWid;
BtnRect.Right := BtnRect.Left;
BtnRect.Top := 0;
BtnRect.Bottom := ClientHeight;
end
else
begin
BtnRect.Left := Width - (Width - ClientWidth) div 2 - BorderWid -
FUpdown.Width;
BtnRect.Right := BtnRect.Left + FUpdown.Width;
BtnRect.Top := 0;
BtnRect.Bottom := ClientHeight;
FUpdown.BoundsRect := BtnRect;
end;
// 編集領域の調整
EditRect := Rect(0, 0, BtnRect.Left - 2, ClientHeight + 1);
SendMessage(Handle, EM_SETRECT, 0, LPARAM(@EditRect));
end;
73-94行目まではボタンが編集ボックスの右端に表示されるよう、位置を計算して、ボタンの座標を調整している部分です。
97、98行目が、この処理で分かりづらい部分でしょう。
ボタンを単に右端に表示しただけでは、編集ボックスの編集部分とボタンが重なってしまいます。数字とボタンが重なって表示されてしまい、数字が見えなくなってしまいます。そこで、編集可能な領域を調整して、ボタンと文字が重ならないようにします。この記事の最初の画像を見て分かるように、数字の 0
が Updown
ボタンと重なっていません。
このようにするには、SendMessage
関数を使用して、新しい編集領域をウィンドウコントロールに通知する必要があります。SendMessage関数についての詳しい説明は省きます。SendMessage
関数は Windows API
と呼ばれる、Windows
のいろいろな情報を設定・取得するための関数の一つです。
さて、ボタンの位置とサイズを調整できるようになったので、SetVisibleUpdownメソッドに以下の1文を追加しましょう(279行目)。
procedure TCustomNumberEdit.SetVisibleUpdown(const Value: Boolean);
begin
if Value then
begin
if FUpdown = nil then CreateUpDown;
FUpdown.Visible := True;
end;
AdjustUpDown;
end;
VisibleUpdownに False が設定された場合
次は、VisibleUpdown
プロパティに False
が設定された場合の処理です。
この処理は簡単で、Updownボタンが存在すれば、それを非表示にするだけです。
SetVisibleUpdown
メソッドを以下のように修正します。
procedure TCustomNumberEdit.SetVisibleUpdown(const Value: Boolean);
begin
if Value then
begin
if FUpdown = nil then CreateUpDown;
FUpdown.Visible := True;
end
else
if FUpdown <> nil then FUpdown.Visible := False;
AdjustUpDown;
end;
これでとりあえず、Updownボタンが表示できるようになったはずです。
「はず」というのは、Updownボタンが表示できるかどうかテストできないからです。VisibleUpdownプロパティのスコープを protected にしたので、アプリケーションからこのプロパティにアクセスできず、VisibleUpdownを True に設定する手段がありません。
次回は、VisibleUpdownをアプリケーションから設定できるようにしたいと思います。