Prefixプロパティの追加 – Delphiカスタムコンポーネントの作成 16

投稿者: | 2018年5月13日

Prefixプロパティを作成します。Prefixプロパティに任意の文字列を設定すると、プレフィックスを表示できるようにします。プレフィックスとは、数字の先頭に表示される語句です。

プレフィックス

また、プレフィックスの表示をON/OFFできるよう、VisiblePrefixプロパティも作成したいと思います。

プレフィックスの実装方法

プレフィックスを数値の前に表示する方法を考えておきます。この方法には3とおりが考えられます。
1つめの方法
1つめの方法は、単純にPrefixとValueをつなげて、1つの文字列にしてしまう方法です。プログラムで書くと以下のようになります。
Text := FPrefix + FloatToStr(Value);
この方法は表示が簡単ですが、ユーザーの入力をチェックする際は手間がかかります。また、ユーザーによっては数値を入力する際、プレフィックを消さないようにと余計な気を遣うことがあるかもしれません。
2つめの方法
2つめの方法は、背景の一部として描画する方法です。単なる背景の一部として描画されるので、ユーザーが選択したり編集したりできません。ユーザーが入力の際に、余計な気を遣うこともないでしょう。ただし、描画されたプレフィックスと、編集可能な数値が重なって表示されないように工夫が必要です。
この方法ではコントロールのキャンバスに文字列を描画することになるため、キャンバスへのアクセス方法を用意しなければなりません。TCustomControlクラスの派生クラスであれば、Canvasプロパティによってキャンバスへアクセスできますが、TCustomEditクラスにCanvasプロパティはありません。これを自前で実装する必要があります。
3つめの方法
3つめの方法は、ラベルコントロールなどを編集ボックスの内側に配置する方法です。編集ボックス内に Align := alLeft で配置すれば、表示や幅の変更が手軽に行えます。ユーザーがプレフィックスの文字列を編集することもありません。Updownボタンを内側に配置したのと同じ方法です。

TNumberEditでは3つめの方法を採用します。

プレフィック用ラベルコントロールの定義

プレフィックの表示にはラベルコントロールを使用します。既存のTLabelを使用することもできますが、プロパティの調整が最小で済むようにTCustomLabelから派生した独自のラベルコントロールを作成します。

TPrefixLabelクラスの定義

プレフィックスに使用するラベルコントロールは、名前を TPrefixLabel にします。なお、これは後の Suffix プロパティの実装にも使用します。コードは以下のとおりです。

  // プレフィックス・サフィックス用ラベル
  TPrefixLabel = class(TCustomLabel)
  private
  protected
  public
  end;

必要な機能はすべてそろっているので、追加するプロパティやメソッドなどはありません。ただし、あとでプロパティの初期値を調整する必要はあるかもしれません。

Prefixプロパティの追加

FPrefixLabelフィールドの追加

TPrefixLabelのインスタンスを保持しておくために、FPrefixLabelフィールドをTCustomNumberEditクラスに追加します。以下のように記述します(32行目)。

  TCustomNumberEdit = class(TCustomEdit)
  private
    { Private 宣言 }
    FComma: Boolean;
    FDigits: Integer;
    FIncrement: Extended;
    FOnUpdownClick: TNotifyEvent;
    FPrefixLabel: TPrefixLabel;

CreatePrefixLabelメソッドの追加

ラベルを表示させるには、TPrefixLabelのインスタンスを生成する必要があります。このインスタンスを生成するためのメソッドを、先に作成しておきましょう。メソッド名を CreatePrefixLabel とします。

CreatePrefixLabelメソッドの定義

定義は以下のようになります(36行目)。

  TCustomNumberEdit = class(TCustomEdit)
  private
    { Private 宣言 }
    FComma: Boolean;
    FDigits: Integer;
    FIncrement: Extended;
    FOnUpdownClick: TNotifyEvent;
    FPrefixLabel: TPrefixLabel;
    FUpdown: TInnerUpdown;
    FValue: Extended;
    procedure AdjustUpDown;
    procedure CreatePrefixLabel;

CreatePrefixLabelメソッドの実装

CreatePrefixLabelメソッドではTPrefixLabelクラスのインスタンスを生成します。また、親(Parent)を自分自身(TCustomNumberEdit)に設定して、内側に表示されるよう設定します。コードは以下のとおりです。

procedure TCustomNumberEdit.CreatePrefixLabel;
begin
  if FPrefixLabel <> nil then Exit;

  FPrefixLabel := TPrefixLabel.Create(Self);
  FPrefixLabel.Parent := Self;
  FPrefixLabel.Align := alLeft;
end;

Prefixプロパティの定義

Prefixプロパティを定義します。スコープは public にします。定義は以下のとおりです(67行目)。

  public
    { Public 宣言 }
    property Comma: Boolean read FComma write SetComma;
    property Digits: Integer read FDigits write SetDigits;
    property Prefix: string read GetPrefix write SetPrefix;

GetPerfixメソッド

プロパティの読み取りは、GetPrefixメソッドを使用します。

GetPrefixメソッドの定義

GetPrefixメソッドは privete 部で以下のように定義します(39行目)。

  procedure CreateUpDown;
    function GetPrefix: string;
    function GetVisibleUpdown: Boolean;

GetPrefixメソッドの実装

実装は以下のとおりです。

function TCustomNumberEdit.GetPrefix: string;
begin
  if FPrefixLabel = nil then
    Result := ''
  else
    Result := FPrefixLabel.Caption;
end;

SetPrefixメソッド

続いてSetPrefixメソッドを作成します。

SetPrefixメソッドの定義

SetPrefixメソッドの定義は以下のとおりです(43行目)。

  procedure SetDigits(const Value: Integer);
    procedure SetPrefix(const Value: string);
    procedure SetValue(const Value: Extended);

SetPrefixメソッドの実装

SetPrefixメソッドではFPrefixLabelのキャプションに文字列を設定したあと、ラベルと数値が重なって表示されないように、編集領域を調整する必要があります。
SetPrefixメソッドの実装部分を記述します。コードは以下のとおりです。AdjustEditRectメソッドはこのあと作成します。

procedure TCustomNumberEdit.SetPrefix(const Value: string);
begin
  CreatePrefixLabel;
  FPrefixLabel.Caption := Value;
  AdjustEditRect;
end;

AdjustEditRectメソッドの作成

編集ボックス内の編集可能な領域を、AdjustEditRectメソッドで調整します。
編集可能な領域は、プレフィックスのほか、Updownボタンと、あとで作成するSuffixプロパティも考慮する必要があります。そのため、AdjustEditRectメソッドで一括して調整した方が効率がよくなります。

AdjustEditRectメソッドの定義

AdjustEditRectメソッドは protected スコープで定義します(49行目)。

  protected
    { Protected 宣言 }
    procedure AdjustEditRect;

AdjustEditRectメソッドの実装

AdjustEditRectメソッドの実装部分は、以下のとおりです。

procedure TCustomNumberEdit.AdjustEditRect;
var
  R: TRect;
begin
  R := AdjustUpdown;

  if FPrefixLabel <> nil then R.Left := R.Left + FPrefixLabel.Width;

  SendMessage(Handle, EM_SETRECT, 0, LPARAM(@R));
end;

97行目、AdjustUpdownメソッドは手続きなので、戻り値はありません。このままでは例外が発生するので、AdjustUpdownメソッドを修正します。

AdjustUpdownメソッドの修正

定義の修正

まず、定義部分を修正します。以下のようになります。

function AdjustUpDown: TRect;

procedure を function に変更し、戻り値に TRect 型を指定します。

実装部の修正

AdjustUpdownメソッドの実装は、以下のように修正します(104、139行目)。

function TCustomNumberEdit.AdjustUpDown: TRect;
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);
  Result := EditRect;
end;

その他の修正

編集領域の調整を AdjustEditRect メソッドで行うようになったため、今まで AdjustUpdown メソッドを呼び出していた部分を修正する必要があります。

CreateHandleメソッドの修正

CreateHandleメソッドの実装部を、以下のように修正します(206行目)。

procedure TCustomNumberEdit.CreateHandle;
begin
  inherited;
  AdjustEditRect;
end;
SetVisibleUpdownメソッドの修正

SetVisibleUpdownメソッドの実装部を、以下のように修正します(401行目)。

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;

  AdjustEditRect;
end;

TPrefixLabelの調整

ここまででプレフィックを表示できるようになりました。テストして若干気になる部分がありますので、調整しましょう。
気になる箇所は1つだけで、プレフィックスの縦方向の文字配置です。縦方向の配置はLayoutプロパティで指定でき、Layoutプロパティの初期値は tlTop です。初期値を tlTop から tlCenter に変更します。

Createコンストラクターの定義

プロパティの初期値を設定するのは Create コンストラクターが適しています。Createコンストラクターをオーバーライドして、Layoutプロパティの初期値を変更します。
Createコンストラクターの定義は以下のとおりです(22行目)。

  TPrefixLabel = class(TCustomLabel)
  private
  protected
  public
    constructor Create(AOwner: TComponent); override;
  end;

Createコンストラクターの実装

実装は以下のとおりになります。

constructor TPrefixLabel.Create(AOwner: TComponent);
begin
  inherited;
  Layout := tlCenter;
end;

これで、TPrefixLabelのLayoutの初期値が tlCenter になりました。

VisiblePrefixプロパティの作成

VisiblePrefixプロパティを作成します。VisiblePrefixプロパティは Boolean 型のプロパティです。Trueでプレフィックスを表示し、Falseでプレフィックスを隠します。

VisiblePrefixプロパティの定義

VisiblePrefixプロパティを public スコープで定義します。コードは以下のとおりです(70行目)。

  public
    { Public 宣言 }
    property Comma: Boolean read FComma write SetComma;
    property Digits: Integer read FDigits write SetDigits;
    property Prefix: string read GetPrefix write SetPrefix;
    property Value: Extended read FValue write SetValue;
    property VisiblePrefix: Boolean read GetVisiblePrefix write SetVisiblePrefix;

GetVisiblePrefix、SetVisiblePrefixメソッドの定義

read部の GetVisiblePrefix メソッドと、writer部の SetVisiblePrefix メソッドの定義は以下のとおりです。

  function GetPrefix: string;
    function GetVisiblePrefix: Boolean;
    function GetVisibleUpdown: Boolean;
    procedure SetComma(const Value: Boolean);
    procedure SetDigits(const Value: Integer);
    procedure SetPrefix(const Value: string);
    procedure SetValue(const Value: Extended);
    procedure SetVisiblePrefix(const Value: Boolean);
    procedure SetVisibleUpdown(const Value: Boolean);

GetVisiblePrefixメソッドの実装

GetVisiblePrefixメソッドの実装は以下のとおりです。

function TCustomNumberEdit.GetVisiblePrefix: Boolean;
begin
  if FPrefixLabel = nil then
    Result := False
  else
    Result := FPrefixLabel.Visible;
end;

SetVisiblePrefixメソッドの実装

SetVisiblePrefixメソッドの実装は以下のとおりです。

procedure TCustomNumberEdit.SetVisiblePrefix(const Value: Boolean);
begin
  CreatePrefixLabel;
  FPrefixLabel.Visible := Value;
  AdjustEditRect;
end;

AdjustEditRectメソッドの修正

AdjustEditRectメソッドではプレフィックスの表示・非表示状態を考慮していませんでしたので、これを修正します(101-103行目)。

procedure TCustomNumberEdit.AdjustEditRect;
var
  R: TRect;
begin
  R := AdjustUpdown;

  if FPrefixLabel <> nil then
    if FPrefixLabel.Visible then
      R.Left := R.Left + FPrefixLabel.Width;

  SendMessage(Handle, EM_SETRECT, 0, LPARAM(@R));
end;

以上で、プレフィックスの実装が完了しました。

コメントを残す

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

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