自作コンポーネントの順序型と集合型のプロパティの初期化の仕方Tipコンポーネントを自作する時に注意するべき点の一つに、Published な順序型と集合型のプロパティの初期設定があります。プロパティ の宣言で指定した デフォルト値と、コンポーネントのコンストラクタで設定する初期設定値が一致していないと、コンポーネントの動きがおかしくなります。 実は Published な順序型と集合型のプロパティは「常に」デフォルト値を持っていますので、必ずコンストラクタで初期化が必要です(解説参照) 適切な初期化を行うには Published な プロパティ の Run-Time Type Infomation を読み取ってデフォルト値を設定するのが適切です。 設定用のルーチン InitPropWithDefault を以下に示します。このルーチンは汎用的でどんなコンポーネントのコンストラクタの中でも使えます。 unit PropUtil; interface procedure InitPropWithDefault(obj: TObject; PropName: string); implementation uses SysUtils, TypInfo; procedure InitPropWithDefault(Obj: TObject; PropName: string); var ppi: PPropInfo; begin ppi := GetPropInfo(obj.ClassInfo, PropName); if ppi = nil then Exception.Create('プロパティが有りません'); if not (ppi.PropType^.Kind in [tkInteger, tkChar, tkWChar, tkSet, tkEnumeration]) then raise Exception.Create('プロパティはデフォルト値を持てない型です'); SetOrdProp(obj, ppi, ppi.Default); end; end. 使用例 type TGaugeEx = class(TGraphicControl) (略) public constructor Create(AOwner: TComponent); override; published { Published 宣言 } property BackColor: TColor read FBackColor write SetBackColor default clWhite; property Color: TColor read FColor write SetColor default clBlack; property Style: TGaugeExStyle read FStyle write SetStyle default gesRectangle; property OVNormalColor: TColor read FOVNormalColor write SetOVNormalColor default clPurple; property OVHiliteColor: TColor read FOVHiliteColor write SetOvHiliteColor default clYellow; property ZeroCenter: Boolean read FZeroCenter write SetZeroCenter default False; end; procedure Register; implementation uses PropUtil; constructor TGaugeEx.Create(AOwner: TComponent); begin inherited Create(AOwner); Width := 100; Height := 65; InitPropWithDefault(Self, 'BackColor'); InitPropWithDefault(Self, 'Color'); InitPropWithDefault(Self, 'Style'); InitPropWithDefault(Self, 'OVNormalColor'); InitPropWithDefault(Self, 'OVHiliteColor'); InitPropWithDefault(Self, 'ZeroCenter'); end; 解説32bit以下の順序型か、あるいは32個以下の要素を持つ集合型のプロパティは常にデフォルト値を持ちます。 プロパティ が default指令を持たない場合は デフォルト値は序数値で表すと $80000000 になるので注意してください。これは Integer では -2147483648、LongWord では 2147483648 、 32個の要素をもつ集合では type TBarEnum = (e0, e1, e2, e3, e4, e5, e6, e7, e8, e9, e10, e11, e12, e13, e14, e15, e16, e17, e18, e19, e20, e21, e22, e23, e24, e25, e26, e27, e28, e29, e30, e31); TBarSet = set of TBarEnum; TBarSet型では [e31] になります。 コンストラクタでプロパティを初期化する際、プロパティはデフォルト値で初期化しなければなりません。 尚、default指令無し場合の上記のデフォルト値が設計時に使われることがありえないような場合は、プロパティを適当な値に初期化してしまってかまいません(上記のWidth や Height プロパティなど)。しかし、設計時に入力されることがありえる場合で、その値が初期値としては不適切な場合は、必ず default指令で適切な初期値を指定してください。 例えば property Foo: Longword; で 2147483648 の入力がありえ、2147483648 が初期値としてふさわしくない場合、default指令が必要になります。 Published 部を持つコンポーネントの VMT には RTTI (実行時型情報)を指すポインタが含まれます。VCL にはこのポインタを介して RTTI にアクセスするためのルーチン群が TypInfo ユニットに定義されています。(しかしこれらは Delphi のドキュメントに記載されていません)。 GetPropInfo は オブジェクトの型情報から、Published な プロパティ の情報を引き出してくれる便利な関数 です。プロパティ 情報(TPropInfo レコード)の Default フィールドからプロパティのデフォルト値を簡単に得ることが出来ます。 SetOrdProp はオブジェクトの Published な プロパティ に、プロパティ 情報を使って値(序数値)をセットしてくれる便利な 手続き です。 尚、蛇足ですが、コンポーネントの Width/Height プロパティは default 指令でデフォルト値を指定してはいけません。Width/Height プロパティは設計時にかならずフォームファイルに記録される必要が有るからです。記録されない場合、フォームの Scaled プロパティがTrueのときに起こるスケーリングが正常に動作しなくなります。ご注意を! |