このページでは、カスタムコンポーネントのプロパティの定義について説明します。
おしながき.
継承プロパティの公開.
継承プロパティをそのまま公開.
基本的なプロパティについては、上位クラスが提供しているプロパティを、そのままコンポーネントのユーザに公開したい場合があります。しかし、単に派生クラスを作っただけでは上位クラスのプロパティは公開されず、コンポーネントのユーザは使用できません。上位クラスから継承したプロパティを公開したい場合、__published部またはpublic部で再宣言する必要があります。
継承プロパティを公開するには以下のようにします。単に__propertyキーワードに続けて公開したいプロパティのプロパティ名を記述するだけです。
__property ShowHint;
継承プロパティの初期値を変更して公開.
継承プロパティの初期値を変更したい場合、次のように初期値を再定義します。初期値の効果に関してはフォームファイルへの保存を参照してください。
ただし、ここで定義した初期値は、プログラマが初期値からオブジェクトインスペクタで値を変更したかどうかを判断するためだけに使用されます。そのため、初期値を再定義しただけでは、実際にプロパティの初期値が変更されるわけではありません。コンストラクタで新しい初期値を代入するなどの対処が必要です。
__property ShowHint = {default=true};
継承プロパティの初期値を削除.
初期値を持つ継承プロパティの、初期値を削除してプロパティを公開したい場合、nodefaultキーワードを記述します。初期値を持たないプロパティの効果に関してもフォームファイルへの保存を参照。
__property ShowHint = {nodefault};
プロパティの追加.
上位クラスに含まれないプロパティは、作成するクラスで追加する必要があります。プロパティはどの型でも作ることが出来ます。
上位クラスに含まれるプロパティに、上位クラスとは別の機能を実装したい場合も、本章で説明する方法でプロパティをオーバーライドすることで実現できます。
プロパティに必要なもの.
1つのプロパティは、複数の要素から構成されます。以下がその構成要素のリスト。各要素は必要ないなら省略が可能です。
要素 | 説明 |
プロパティ値を保持するメンバ | プロパティ値を実際に保持する。 |
readメソッド | プロパティ値の読み出し時に呼び出されるメソッド。 |
writeメソッド | プロパティ値の書き込み時に呼び出されるメソッド。 |
値の保存の指定 | パブリッシュプロパティ値のフォームファイルへの保存を指定する。 |
値の保存と読み込み | パブリッシュプロパティ値のフォームファイルへの保存・読み込み時に呼び出されるメソッド。 |
プロパティの実体は、通常は、プロパティと同じ型のプライベートメンバと、それを読み出し・書き込みするアクセスメソッドで構成されます。プロパティに対する読み出し・書き込みは、C++Builderによって対応するアクセスメソッドの呼び出しに変換されます。つまりC++Builderが直接アクセスするのはアクセスメソッドのみであり、アクセスメソッドが必要としないなら、プロパティ値を保持するメンバは必要ありません。
プロパティの公開方法.
プロパティの定義を記述する場所により、プロパティの機能性が異なります。
__published部 | public部 | |
コードからの値の参照・変更 | ○ | ○ |
---|---|---|
オブジェクトインスペクタでの表示・編集 | ○ | × |
フォームファイルへの値の保存・読み込み | ○ | △(DefinePropertiesメソッドのオーバーライドにより可能) |
新しいプロパティの定義.
プロパティの定義は、ヘッダのクラス定義内で行います。PagePanelからTPagePanelクラスのActivePageプロパティを抜粋すると、次のようになります。
__published: __property TPageSheet * ActivePage = {read=getActivePage, write=setActivePage};
定義の内容は、プロパティの宣言を表す__propertyキーワードで修飾して、型・プロパティ名・アクセスメソッドを記述します。__propertyキーワードはプロパティの宣言を表すものなので、必ず必要です。
プロパティの型は、読み出したときに返される値の型です。同時に、書き込む値の型でもあります。関数の戻り値と同様、型は何でもかまいません。
プロパティ名は、クラス内でプロパティを一意に識別するための識別子です。上位クラスが定義したプロパティと同じ識別子を使うと、上位クラスのプロパティをオーバーライドします。
中カッコの中はアクセスメソッドの定義です。readに指定したものはこのプロパティの値を読み出すためのreadメソッド、writeに指定したものはこのプロパティに値を書き込むためのwriteメソッドです。ここにフォームファイルへの値の保存に関する記述も行えますが、この例では省略しています。詳しくはフォームファイルへの保存を参照。
アクセスメソッドの定義宣言.
次のソースは、ヘッダのクラス定義内で行うアクセスメソッドの宣言の例です。上記のTPagePanelクラスの定義からActivePageプロパティからの抜粋です。
protected: virtual TPageSheet * __fastcall getActivePage(void); virtual void __fastcall setActivePage(TPageSheet *activePage);
protected部で定義することで、他のモジュールから隠蔽するとともに、下位クラスからは利用できるようにします。アクセスメソッドは通常virtualキーワードで仮想メソッドにします。
2行目はreadメソッドの宣言です。
- メソッド名は、プロパティ名の前にGetを付けたものが一般的。
- 戻り値の型は、プロパティの型と同じでなければならない。
- プロパティがスカラ型の場合、引数はない。
- readメソッドを省略すると、書き込み専用のプロパティになる(ほとんど使われない)。
プロパティの読み出しが、値を保持しているメンバから値を読み出すだけであれば、readメソッドの実装を省略できます。その場合はプロパティの定義においてreadキーワード後に、readメソッドの代わりに、値を保持しているデータメンバを記述します。例として、FontNameBoxからTFontNameComboBoxクラスのIconプロパティを抜粋します。writeメソッドは普通に定義していますが、readメソッドは直接値を保持しているデータメンバにアクセスしています。
private: bool fIcon; protected: virtual void __fastcall setIcon(bool icon); __published: __property bool Icon = {read=fIcon, write=setIcon, default=true};
最初の例の4行目はwriteメソッドの宣言です。
- メソッド名は、プロパティ名の前にSetを付けたものが一般的。
- 戻り値はないのでvoid型。
- プロパティの値として書き込まれる値が、最初の引数。
- writeメソッドを省略すると、読み出し専用のメソッドになる(パブリッシュプロパティではまず使われない)。
readメソッドと同様に、プロパティへの書き込みに関して必要な処理が、値を保持するメンバに値を書込むだけであれば、writeメソッドの実装を省略できます。その場合はプロパティの定義においてwriteキーワードの後に、writeメソッドの代わりに、値を保持するデータメンバを記述します。
アクセスメソッドの実体定義.
readメソッドの実体は、プロパティに対応する値を返すだけなので簡単だと思います。
writeメソッドの実体では、プロパティの機能仕様にもよりますが、一般的には次のような処理を行います。
- 書き込む値のチェック。不正な値の場合は正規化もしくは無視。
- 書き込む値を保存。
- 書き込む値に応じて、コンポーネントの状態を変更する必要があれば、変更する。
配列プロパティ.
プロパティが配列である場合は、配列の個々の要素を識別するためのインデックスを、引数に追加します。このインデックスは、整数型である必要はありません。インデックスの値の解釈はアクセスメソッドに任されるので、整数型以外でも使用できるようになっています。またインデックスを複数指定することで多次元配列に対応します。
配列プロパティは、普通の配列と違って、配列へのポインタを使用して全要素にアクセスすることは出来ません。個々の要素を1つずつアクセスできるだけです。
コンポーネントを参照するプロパティの注意点.
問題.
プロパティの型が何らかのコンポーネントであるような、コンポーネントを参照するプロパティの場合、参照先のインスタンスの寿命に注意してコーディングする必要があります。プロパティが参照しているインスタンスが、実行時に動的に削除された場合、それ以降プロパティ値の参照は予測できない障害を発生することになります(たいていの場合はアクセス違反でしょうが)。
カスタムコンポーネントを使用するアプリケーションプログラムの作りによっては、このようなケースはないかもしれません。しかしどの様な使い方をされるかわからないのがコンポーネントです。このような場合に対処する必要もあります。
対策.
TComponent::Notificationメソッドをオーバーライドすることにより、この問題に対処することができます。
コンポーネントの生成・削除が行われると、TComponent::Notificationメソッドがコールされます。オーバーライドしたメソッドで、参照先を無効にする処理を追加します。
Copyright 2005-2016, yosshie.