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 型に限らず、デフォルトを持てない全ての プロパティ の型で使える手法です。 |