戻る ホーム 上へ 進む

Double型の property のデフォルト値を 0.0 以外にしたい

Tip & 解説

コンポーネントを自作する際、Delphi には プロパティ のデフォルト値に奇妙な制限が有ります。整数型や、列挙型などではデフォルト値(設計時にフォームにドロップした時のコンポーネントの プロパティ の値)を default 指令 を使って自由に指定できるのに、Double 型などの プロパティ ではデフォルト値が無条件に 0.0 になってしまいます。このため、自作コンポーネントのコンストラクタで、 Double 型の プロパティ を 0.0 以外に設定すると不具合が起きます。例えば

type
  class MyControl = class(TGraphicControl)
  private
    FValue: Double;
    .
    .
  published
    property Value: Double read FValue write SetValue;
    .
    .
  end;
.
.
constructor MyControl.Create(AOwner: TComponent)
begin
  FValue := 0.5;
  .
  .
end;

という具合に、コンポーネントの Double 型の プロパティ 「Value」 を無理矢理コンストラクタで 0.5 に設定すると、このコンポーネントをプロジェクトのフォームにドロップした時は Value プロパティ が 0.5 になるので、一見、よさそうに見えますが、Value プロパティ の値を 0.0 に変更してプロジェクトをセーブし、再びこのプロジェクトを開くと、コンポーネントの Value プロパティ の値は 0.5 に戻ってしまいます。

原因は Double 型の プロパティ 値のデフォルト値が 無条件に 0.0 になるため、 Value プロパティ の値が 0.0 の場合、値がフォームファイルにセーブされないためです。プロジェクトをいったんクローズし、再オープンすると、Value プロパティ はコンストラクタで 0.5 に初期化されます。その後、フォームファイルから Value プロパティ の値がロードされるわけですが、フォームファイルに値がセーブされていないため、値が 0.0 にはならず、0.5 のままになってしまいます。つまり、プロジェクトをクローズする前とプロジェクトを再オープンした後で、プロパティ の値が異なってしまうのです。

この問題はプログラムの実行時にも起こります。実行時はフォームファイルが実行ファイル中のリソースに置き換わるだけだからです。

この問題は、コンポーネントの ReadState メソッドを override することで対処可能です。この方法は、Delphi メーリングリストのメンバ 武内さんが考えられたものです。

対処のコーディング例を以下に示します。

type
  class MyControl = class(TGraphicControl)
  private
    bReadStateCalled: Boolean; {ReadState が既に Call されているかを示すフラグ}
                               {Delphi 2.0J の派生フォーム対策}
    FValue: Double;
    .
    .
  protected
    {ReadState を Override する}
    procedure ReadState(Reader: TReader); override;
  published
    property Value: Double read FValue write SetValue;
    .
    .
  end;
.
.

constructor MyControl.Create(AOwner: TComponent)
begin
  FValue := 0.5;
  .
  .
end;

procedure MyControl.ReadState(Reader: TReader);
begin
  {コンストラクタで初期化されて後の、最初の ReadState の呼び出しで
   Value property を 0.0 に初期化する。}
  if not bReadStateCalled then begin
    bReadStateCalled := True;
    FValue := 0.0;
  end;
  inherited ReadState(Reader);
end;

対処の考え方は単純です。コンストラクタは設計時にコンポーネントをフォームにドロップした時の Double 型の プロパティ の初期値の与えるものとし、フォームファイルを読み取る時や、実行時にリソースファイルを読み取る時は ReadState メソッドで Double 型 プロパティ の値を 0.0 に初期化してから プロパティ 値をロードするようにします。こうすれば、上記の矛盾は発生しません。

但し、単純に ReadState メソッドで プロパティ を初期化するとフォーム継承を使う時、問題が起きます。フォーム継承は、継承元から順番に複数のフォームファイル(実行時はリソース内のフォーム情報)を読み込み、フォーム内の各コンポーネントの各 プロパティ 値を重ね書きするので、コンポーネントの ReadState メソッドがその度に呼び出される可能性があります。また、派生元から派生して作られたフォームのフォームファイルには、派生元との差分しか記録されないので、 ReadState メソッドが呼ばれるたびに、プロパティ 値を初期化してしまうとまずいことになります。

そこでコンポーネントにフラグを設けて、最初にコンポーネントの ReadState が呼ばれた時だけ、 Double 型の プロパティ を 0.0 に初期設定するようにし、2回目以降は初期化しないようにしました。この方法は、Double 型に限らず、デフォルトを持てない全ての プロパティ の型で使える手法です。

戻る ホーム 上へ 進む inserted by FC2 system