Note
この本は 2016 年春に発行されて以降、改訂されていません。 多くの情報はまだ価値がありますが、一部の資料は古くなっており、トピックの中にはまったく正しくないものまたは不完全なものもあります。
すべての C# プログラマは、C# の "プロパティ" について理解しています。 プロパティには、set アクセサーと get アクセサーのどちらか一方または両方が含まれます。 それらは、共通言語ランタイムの "CLR プロパティ" と呼ばれることがよくあります。
Xamarin.Forms では、BindableProperty クラスによってカプセル化され、BindableObject クラスによってサポートされる、"バインド可能プロパティ" と呼ばれる拡張プロパティ定義が定義されています。 これらのクラスは関連していますが、全く異なるものです。BindableProperty は、プロパティ自体を定義するために使用されます。BindableObject は、バインド可能プロパティを定義するクラスの基底クラスであるという点で object に似ています。
Xamarin.Forms のクラス階層
ClassHierarchy サンプルでは、リフレクションを使用して Xamarin.Forms のクラス階層が表示され、この階層において BindableObject によって果たされる重要な役割が示されています。 BindableObject は、Object から派生し、それを親クラスとする Element から、VisualElement が派生します。 さらにこれを親として Page と View が派生し、それは Layout に対する親クラスです。
BindableObject と BindableProperty の詳細
BindableObject から派生したクラスでは、多くの CLR プロパティがバインド可能プロパティ "によってサポートされている" と言われます。 たとえば、Label クラスの Text プロパティは CLR プロパティですが、Label クラスでは、BindableProperty 型の TextProperty という名前のパブリックな静的読み取り専用フィールドも定義されています。
アプリケーションでは、普通に Label の Text プロパティを設定または取得できます。または、Label.TextProperty 引数を使用して、BindableObject によって定義された SetValue メソッドを呼び出すことにより、Text を設定することもできます。 同様に、アプリケーションでは、やはり Label.TextProperty 引数を使用して、GetValue メソッドを呼び出すことにより、Text プロパティの値を取得できます。 これについては、PropertySettings サンプルを参照してください。
実際には、Text CLR プロパティは、BindableObject と Label.TextProperty 静的プロパティの組み合わせによって定義された SetValue メソッドと GetValue メソッドを使用して、完全に実装されます。
BindableObject と BindableProperty では、以下に対するサポートが提供されます。
- プロパティの既定値の指定
- 現在の値の格納
- プロパティ値を検証するためのメカニズムの提供
- 1 つのクラスの関連するプロパティ間の整合性の維持
- プロパティの変更への応答
- プロパティが変更されようとしているとき、または変更されたときの通知のトリガー
- データ バインディングのサポート
- スタイルのサポート
- 動的リソースのサポート
バインド可能プロパティによってサポートされるプロパティが変更されるたびに、BindableObject によって、変更されたプロパティを示す PropertyChanged イベントが生成されます。 プロパティが同じ値に設定されたときは、このイベントは生成されません。
一部のプロパティは、バインド可能プロパティによってサポートされていません。また、Span などの一部の Xamarin.Forms クラスは、BindableObject から派生していません。 BindableObject で SetValue メソッドと GetValue メソッドが定義されているため、BindableObject から派生したクラスのみがバインド可能プロパティをサポートできます。
Span は BindableObject から派生していないため、Text などのプロパティはどれも、バインド可能プロパティによってサポートされていません。 このため、Span の Text プロパティで DynamicResource を設定すると、前の章の DynamicVsStatic サンプルで例外が発生します。 DynamicVsStaticCode サンプルでは、Element によって定義された SetDynamicResource メソッドを使用して、コードで動的リソースを設定する方法が示されています。 最初の引数は BindableProperty 型のオブジェクトです。
同様に、BindableObject で定義された SetBinding メソッドの最初の引数は、BindableProperty 型です。
バインド可能プロパティの定義
静的メソッド BindableProperty.Create を使用して BindableProperty 型の静的読み取り専用フィールドを作成し、独自のバインド可能プロパティを定義できます。
これについては、Xamarin.FormsBook.Toolkit ライブラリの AltLabel クラスで示されています。 そのクラスは Label から派生し、フォント サイズをポイント単位で指定できます。 PointSizedText サンプルを参照してください。
BindableProperty.Create メソッドの 4 つの引数は必須です。
propertyName: プロパティのテキスト名 (CLR プロパティ名と同じ)returnType: CLR プロパティの型declaringType: プロパティを宣言するクラスの型defaultValue: プロパティの既定値
defaultValue は object 型であるため、コンパイラは既定値の型を決定できる必要があります。 たとえば、returnType が double である場合、defaultValue は単に 0 ではなく 0.0 のように設定する必要があります。そうしないと、型の不一致によって実行時に例外が発生します。
また、バインド可能プロパティに次のものが含まれるのもよくあることです。
propertyChanged: プロパティの値が変更されたときに呼び出される静的メソッド。 最初の引数は、プロパティが変更されたクラスのインスタンスです。
BindableProperty.Create に対する他の引数は、一般的ではありません。
defaultBindingMode: データ バインディングとの関係で使用されます (「第 16 章「データ バインディング」を参照)。validateValue: 有効な値を確認するためのコールバックpropertyChanging: プロパティが変更されようとしていることを示すコールバックcoerceValue: set 値を別の値に強制的に変換するためのコールバックdefaultValueCreate: クラスのインスタンス間で共有できない既定値を作成するためのコールバック (たとえば、コレクション)
読み取り専用のバインド可能プロパティ
バインド可能プロパティは読み取り専用にできます。 読み取り専用のバインド可能プロパティを作成するには、静的メソッド BindableProperty.CreateReadOnly を呼び出して、BindablePropertyKey 型のプライベートな静的読み取り専用フィールドを定義する必要があります。
次に、BindablePropertyKey オブジェクトで SetValue のオーバーロードを呼び出すために、CLR プロパティの set アクセサーを private として定義します。 これにより、プロパティがクラスの外部で設定されるのを防ぐことができます。
これについては、BaskervillesCount サンプルで使用されている CountedLabel クラスで示されています。
