Xamarin.Forms バインド可能プロパティ
バインド可能プロパティは、プロパティをフィールドでバッキングするのではなく、プロパティを BindableProperty
型でバッキングすることで CLR プロパティ機能を拡張します。 バインド可能なプロパティの目的は、親子リレーションシップを介して設定されたデータ バインディング、スタイル、テンプレート、値をサポートするプロパティ システムを提供することです。 さらに、バインド可能なプロパティは、既定値、プロパティ値の検証、プロパティの変更を監視するコールバックを提供できます。
プロパティは、次の 1 つ以上の機能をサポートするために、バインド可能プロパティとして実装する必要があります。
- データ バインディングに有効なターゲット プロパティとしての動作。
- スタイルを使用してプロパティを設定する。
- プロパティの型の既定値とは異なる既定のプロパティ値を指定します。
- プロパティの値の検証。
- プロパティの変更の監視。
Xamarin.Forms バインド可能プロパティの例には、Label.Text
、Button.BorderRadius
、StackLayout.Orientation
があります。 各バインド可能なプロパティには、同じクラスで公開され、バインド可能なプロパティの識別子である BindableProperty
型の対応する public static readonly
フィールドがあります。 たとえば、Label.Text
プロパティに対応するバインド可能プロパティ識別子は Label.TextProperty
です。
バインド可能なプロパティを作成する
バインド可能なプロパティを作成するプロセスは次のとおりです。
BindableProperty.Create
メソッドのオーバーロードのいずれかを使用してBindableProperty
インスタンスを作成します。BindableProperty
インスタンスのプロパティ アクセサーを定義します。
すべての BindableProperty
インスタンスは UI スレッドに作成する必要があります。 つまり、バインド可能なプロパティの値を取得または設定できるのは、UI スレッドで実行されるコードだけです。 ただし、BindableProperty
インスタンスは、Device.BeginInvokeOnMainThread
メソッドを使用して UI スレッドにマーシャリングすることで、他のスレッドからアクセスできます。
プロパティを作成する
BindableProperty
インスタンスを作成するには、包含クラスが BindableObject
クラスから派生している必要があります。 ただし、BindableObject
クラスはクラス階層で高い位置にあるため、ユーザー インターフェイス機能に使用されるクラスの大半はバインド可能プロパティをサポートします。
バインド可能なプロパティは、BindableProperty
型の public static readonly
プロパティを宣言することによって作成できます。 バインド可能なプロパティは、いずれかの BindableProperty.Create
メソッド オーバーロードの戻り値に設定する必要があります。 宣言は BindableObject
派生クラスの本文、かつメンバー定義の外部に記載する必要があります。
少なくとも、BindableProperty
を作成する際には、次のパラメーターと共に識別子を指定する必要があります。
BindableProperty
の名前。- プロパティの型。
- 所有するオブジェクトの型。
- プロパティの既定値。 これにより、プロパティが設定されていない場合は常に特定の既定値が返され、プロパティの型の既定値とは異なる場合があります。 バインド可能プロパティで
ClearValue
メソッドが呼び出されると、既定値が復元されます。
重要
バインド可能なプロパティの名前付け規則は、バインド可能なプロパティ識別子が Create
メソッドで指定されたプロパティ名と一致する必要があり、「Property」が追加されていることです。
次のコードは、4 つの必須パラメーターの識別子と値を持つバインド可能なプロパティの例を示しています。
public static readonly BindableProperty EventNameProperty =
BindableProperty.Create ("EventName", typeof(string), typeof(EventToCommandBehavior), null);
これは string
型の EventNameProperty
という名前の BindableProperty
インスタンスを作成します。 このプロパティは EventToCommandBehavior
クラスが所有し、既定値は null
です。
必要に応じて、BindableProperty
インスタンスを作成するときに、次のパラメーターを指定できます。
- バインド モード。 これは、プロパティ値の変更が反映される方向を指定するために使用されます。 既定のバインド モードでは、変更がソースからターゲットに反映されます。
- プロパティ値が設定されたときに呼び出される検証デリゲート。 詳細については、「検証コールバック」を参照してください。
- プロパティ値が変更されたときに呼び出される、プロパティ変更デリゲート。 詳細については、「Detect property changes」をご覧ください。
- プロパティ値が変更されたときに呼び出されるプロパティ変更デリゲート。 このデリゲートには、プロパティ変更デリゲートと同じシグネチャがあります。
- プロパティ値が変更されたときに呼び出される値強制デリゲート。 詳細については、「Coerce value callbacks」をご覧ください。
- 既定のプロパティ値を初期化するために使用される
Func
。 詳細については、「Func を使用して既定値を作成する」をご覧ください。
アクセサーを作成する
プロパティ アクセサーは、バインド可能なプロパティにアクセスするためにプロパティ構文を使用する必要があります。 Get
アクセサーは、対応するバインド可能なプロパティに含まれている値を返す必要があります。 これを実現するには、GetValue
メソッドを呼び出し、値を取得するバインド可能なプロパティ識別子を渡し、結果を必要な型にキャストします。 Set
アクセサーは、対応するバインド可能プロパティの値を設定する必要があります。 これを実現するには、SetValue
メソッドを呼び出し、値を設定するバインド可能なプロパティ識別子と設定する値を渡します。
次の例では、EventName
バインド可能プロパティのアクセサーを示します。
public string EventName
{
get { return (string)GetValue (EventNameProperty); }
set { SetValue (EventNameProperty, value); }
}
バインド可能なプロパティを使用する
バインド可能プロパティが作成されると、XAML またはコードからそのプロパティを使用できます。 XAML では、これは、CLR 名前空間名を示す名前空間宣言と、必要に応じてアセンブリ名を含むプレフィックスを持つ名前空間を宣言することによって実現されます。 詳細については、「XAML 名前空間」をご覧ください。
次のコード例は、バインド可能なプロパティを含むカスタム型の XAML 名前空間を示しています。この名前空間は、カスタム型を参照しているアプリケーション コードと同じアセンブリ内で定義されています。
<ContentPage ... xmlns:local="clr-namespace:EventToCommandBehavior" ...>
...
</ContentPage>
名前空間宣言は、次の XAML コード例に示すように、EventName
のバインド可能なプロパティを設定するときに使用されます。
<ListView ...>
<ListView.Behaviors>
<local:EventToCommandBehavior EventName="ItemSelected" ... />
</ListView.Behaviors>
</ListView>
これと同じ C# コードの例は次のとおりです。
var listView = new ListView ();
listView.Behaviors.Add (new EventToCommandBehavior
{
EventName = "ItemSelected",
...
});
詳細シナリオ
BindableProperty
インスタンスを作成するときに、高度なバインド可能なプロパティ シナリオを有効にするために設定できる省略可能なパラメーターがいくつかあります。 このセクションでは、これらのシナリオについて説明します。
プロパティの変更を検出する
static
プロパティ変更コールバック メソッドは、BindableProperty.Create
メソッドの propertyChanged
パラメーターを指定することで、バインド可能なプロパティに登録できます。 バインド可能プロパティの値が変更されると、指定したコールバック メソッドが呼び出されます。
次のコード例は、EventName
のバインド可能なプロパティが OnEventNameChanged
メソッドをプロパティ変更コールバック メソッドとして登録する方法を示しています。
public static readonly BindableProperty EventNameProperty =
BindableProperty.Create (
"EventName", typeof(string), typeof(EventToCommandBehavior), null, propertyChanged: OnEventNameChanged);
...
static void OnEventNameChanged (BindableObject bindable, object oldValue, object newValue)
{
// Property changed implementation goes here
}
プロパティ変更コールバック メソッドでは、所有クラスのどのインスタンスが変更を報告したかを示すために BindableObject
パラメーターが使用され、2 つの object
パラメーターの値はバインド可能なプロパティの古い値と新しい値を表します。
検証コールバック
static
検証コールバック メソッドは、BindableProperty.Create
メソッドの validateValue
パラメーターを指定することで、バインド可能なプロパティに登録できます。 バインド可能なプロパティの値が設定されると、指定したコールバック メソッドが呼び出されます。
次のコード例は、Angle
のバインド可能なプロパティが IsValidValue
メソッドを検証コールバック メソッドとして登録する方法を示しています。
public static readonly BindableProperty AngleProperty =
BindableProperty.Create ("Angle", typeof(double), typeof(HomePage), 0.0, validateValue: IsValidValue);
...
static bool IsValidValue (BindableObject view, object value)
{
double result;
bool isDouble = double.TryParse (value.ToString (), out result);
return (result >= 0 && result <= 360);
}
検証コールバックには値を用意し、値がプロパティに対して有効な場合は true
を返し、それ以外の場合は false
を返します。 検証コールバックが false
を返した場合は例外が発生します。これは開発者が処理する必要があります。 検証コールバック メソッドの一般的な用途は、バインド可能なプロパティが設定されている場合に整数または倍精度浮動小数点型の値を制限することです。 たとえば、IsValidValue
メソッドは、プロパティ値が 0 から 360 の範囲内の double
であることを確認します。
値強制コールバック
static
値強制コールバック メソッドは、BindableProperty.Create
メソッドの coerceValue
パラメーターを指定することで、バインド可能なプロパティに登録できます。 バインド可能プロパティの値が変更されると、指定したコールバック メソッドが呼び出されます。
重要
BindableObject
型には、値強制コールバックを呼び出すことによって、BindableProperty
引数の値の再評価を強制するために呼び出すことができる CoerceValue
メソッドがあります。
値強制コールバックは、プロパティの値が変更されたときにバインド可能プロパティの再評価を強制するために使用されます。 たとえば、値強制コールバックを使用して、1 つのバインド可能なプロパティの値が別のバインド可能なプロパティの値より大きくないことを確認できます。
次のコード例は、Angle
のバインド可能なプロパティが CoerceAngle
メソッドを値強制コールバック メソッドとして登録する方法を示しています。
public static readonly BindableProperty AngleProperty = BindableProperty.Create (
"Angle", typeof(double), typeof(HomePage), 0.0, coerceValue: CoerceAngle);
public static readonly BindableProperty MaximumAngleProperty = BindableProperty.Create (
"MaximumAngle", typeof(double), typeof(HomePage), 360.0, propertyChanged: ForceCoerceValue);
...
static object CoerceAngle (BindableObject bindable, object value)
{
var homePage = bindable as HomePage;
double input = (double)value;
if (input > homePage.MaximumAngle)
{
input = homePage.MaximumAngle;
}
return input;
}
static void ForceCoerceValue(BindableObject bindable, object oldValue, object newValue)
{
bindable.CoerceValue(AngleProperty);
}
CoerceAngle
メソッドは、MaximumAngle
プロパティの値をチェックし、Angle
プロパティ値がそれ以上の場合は、その値を MaximumAngle
プロパティ値に強制的に設定します。 さらに、MaximumAngle
プロパティが変更されると、CoerceValue
メソッドを呼び出すことによって Angle
プロパティに対して値強制コールバックが呼び出されます。
Func を使用して既定値を作成する
次のコード例に示すように、Func
を使用してバインド可能プロパティの既定値を初期化できます。
public static readonly BindableProperty SizeProperty =
BindableProperty.Create ("Size", typeof(double), typeof(HomePage), 0.0,
defaultValueCreator: bindable => Device.GetNamedSize (NamedSize.Large, (Label)bindable));
defaultValueCreator
パラメーターは Func
に設定されます。これは、ネイティブ プラットフォーム上で Label
に使用されるフォントの名前付きサイズを表す double
を返す Device.GetNamedSize
メソッドを呼び出します。