内部表示用Updownボタンの作成 – Delphi カスタムコンポーネントの作成 12

投稿者: | 2018年3月17日

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プロパティの追加

TCustomNumberEditVisibleUpdown プロパティを追加します。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行目が、この処理で分かりづらい部分でしょう。
ボタンを単に右端に表示しただけでは、編集ボックスの編集部分とボタンが重なってしまいます。数字とボタンが重なって表示されてしまい、数字が見えなくなってしまいます。そこで、編集可能な領域を調整して、ボタンと文字が重ならないようにします。この記事の最初の画像を見て分かるように、数字の 0Updown ボタンと重なっていません。
このようにするには、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をアプリケーションから設定できるようにしたいと思います。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

日本語が含まれない投稿は無視されますのでご注意ください。(スパム対策)