数値だけを入力できるようにする – Delphiカスタムコンポーネントの作成 7

投稿者: | 2018年2月12日

ここまで、プロパティをプログラムのコードから設定する部分をメインに作成してきました。コードからはValueプロパティを使用して、数値を設定できるようになりました。
しかし、編集ボックスにユーザーが入力する部分についてはそのままで、どのような文字列でも入力できてしまいます。また、適切な数字を入力しても、Valueプロパティに反映されません。
そこで、何度かに分けて、編集ボックスへの入力部分を作っていきたいと思います。予定では、

  1. 数値だけを入力できるようにする
  2. 入力時のカーソル位置を調整する
  3. Valueプロパティに反映する

の順で作成します。

KeyPressメソッドのオーバーライド

ユーザーがキーボードから文字を入力したとき、コンポーネント内のどこかで入力された文字をチェックして、数値として適切なものだけを受け付けるという処理が必要です。
TWinControlから派生したクラスでは、文字の入力があると KeyPress メソッドが実行されます。
KeyPress メソッドは OnKeyPress イベントを処理するメソッドです。ですが、入力された文字が不適切な場合、別の文字へと置き換えることができます。必要であれば、その文字の入力がなかったことにもできます。
KeyPress メソッドは TWinControl クラスのメソッドです。このメソッドに新しい機能(数値だけを受け付ける機能)を追加するには、メソッドをオーバーライドします。

オーバーライドできるメソッドは、仮想メソッド(virtual)か、動的メソッド(dynamic)、またはこれらのメソッドをオーバーライドしたメソッドに限られます。

KeyPressメソッドのオーバーライドは、以下のように記述します(20行目)。

  protected
    { Protected 宣言 }
    procedure KeyPress(var Key: Char); override;

オーバーライドするときはメソッドのタイプ(手続きか関数か)や引数を、オーバーライド元のメソッドに合わせる必要があります。protected で宣言しているのは、元のメソッドも protected で宣言されているからです。

KeyPressメソッドの実装

Keypressメソッドの実装コードを記述します。先ほどの宣言部分(20行目)付近を、マウスで右クリックします。コンテキストメニューに「カーソル位置のクラスを補完」があるので、これを実行します(または、キーボードの [Ctrl]+[Shift]+[C] を押してもよい)。以下のコードが自動的に作成されます。

procedure TCustomNumberEdit.KeyPress(var Key: Char);
begin
  inherited;

end;

自動的に追加されたコードには inherited; があります。これは上位クラスのメソッドを呼び出すためのもので、inherited KeyPress(Key);と同じ意味になります。inherited については今後説明します。今は、オーバーライドしたメソッドに必要なコードと覚えておいてください。

IsValidCharメソッドの作成

ユーザーが入力した文字の検証は、別のメソッドで行います。KeyPressメソッドを簡潔にするためと、下位クラスでオーバーライドしたときに、入力できる文字を調整できるようにするためです。
作成するメソッドは IsValidChar とします。以下のように、protected で仮想メソッドとして定義します(20行目)。

  protected
    { Protected 宣言 }
    function IsValidChar(const Key: Char): Boolean; virtual;

IsValidCharメソッドの実装では Windows の API を使用します。以下のように、interface の uses に、Winapi.Windows を追加します(6行目)。

interface

uses
  System.SysUtils, System.Classes, Vcl.Controls, Vcl.StdCtrls, Winapi.Windows;

IsValidCharメソッドの実装は下記のとおりです。

function TCustomNumberEdit.IsValidChar(const Key: Char): Boolean;
var
  IsControlKey: Boolean;
  Settings: TFormatSettings;
begin
  Settings := TFormatSettings.Create;
  IsControlKey := (GetKeyState(VK_CONTROL) and $8000 <> 0);

  Result := CharInSet(Key,
    [Settings.DecimalSeparator, '+', '-', '0'..'9', #$0008, #$0009, #$000D]) or
    IsControlKey;
end;

IsControlKey変数は、[Ctrl]キーが押されている場合 True になります(50行目)。[Ctrl]キーの状態をチェックしているのは次の理由からです。
たとえば [Ctrl]+[V] キーが押された場合、[V] は数字でないのでキーが無効になります。そのため、ショートカットキーが使用できないという事態になります。これを回避するため、[Ctrl]キーが押されていればショートカットとみなし、どのような文字でも受け入れるようにしています。
[Ctrl]キーが押されていないときの有効な文字は、52行目からの CharInSet 関数で確認しています。Key引数の文字が、[ ] 内の配列に含まれていれば、戻り値は True になります。
入力可能な文字は、小数点(Settings.DecimalSeparator)、プラス記号(+)、マイナス記号(-)、0 から 9 の数字、バックスペース(#$0008)、タブ(#$0009)、リターンキー(#$000D)です。
プラス記号とマイナス記号は、数値のプラスとマイナスを入れ替えるときに使用します。バックスペースは文字を削除するため、タブはコントロールのフォーカスを移動するために使用します。また、リターンキーは入力確定や、フォーカス移動などの処理に使えるようにするため、入力できるようにしておきます。

KeyPressメソッドの実装2

KeyPressメソッドの実装は、以下のようになります。

procedure TCustomNumberEdit.KeyPress(var Key: Char);
var
  S: string;
  Settings: TFormatSettings;
begin
  if not IsValidChar(Key) then Key := #0;
  Settings := TFormatSettings.Create;

  case Key of
    '+', '-':
      begin
        // 数値の先頭にマイナスを入れる(またはマイナスを消す)
        S := Text;

        if S <> '' then
        begin
          if S[1] = '-' then
            S := Copy(S, 2, Length(S) - 1)
          else if (S[1] <> '-') and (Key = '-') then
            S := '-' + S;
        end
        else if Key = '-' then
          S := '-';

        Text := S;
        Key := #0;
      end;

    else
      if Key = Settings.DecimalSeparator then
      begin
        // 小数点を入力可能か
        if Pos(Settings.DecimalSeparator, Text) >= 1 then Key := #0;
      end;
  end;

  inherited;
end;

(62行目)
ユーザーが入力した文字は、Key引数で受け取ります。Key引数をIsValidCharメソッドでチェックして、有効な文字かどうかを確認します。有効ではない(戻り値がFalse)場合、Key引数に空文字(#0)を設定することで、文字入力をなかったことにできます。
(66-83行目)
特に処理が必要な文字を、case文で処理します。ここではプラス記号とマイナス記号の処理を行っています。
プラス記号が入力された場合、負の数値を正の数値に変更します。実際の処理は、先頭のマイナス記号を消去するだけです。
マイナス記号が入力された場合、正負を入れ替えます。正の数だったら先頭にマイナス記号を付けて負の数にします。負の数だったら、先頭のマイナス記号を消去して、正の数にします。
(85-90行目)
小数点の処理です。すでに小数点が入力されていたら、2個目の小数点を無効にしています。
(93行目)
先ほどの inherited です。上位クラスの KeyPress メソッドを実行しています。TCustomNumberEditの上位クラスとは、TCustomEdit です。
TCustomNumberEditのKeyPressメソッドは、入力された文字をチェックして、必要のない文字を取り除いているだけです。有効な文字をTextプロパティに設定して、画面の表示を更新する処理は、上位の TCustomEdit や、さらにその上位のクラスで実装されています。そのため、上位クラスのKeyPressメソッドを実行してやらないと、何を入力しても、編集ボックスの表示が変わらないといった不具合につながります。
上位クラスで実装されている処理を行うために、オーバーライドしたメソッドでは、inherited を実行する必要があります。

コメントを残す

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

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