Xamarin.Forms バインド可能プロパティ

Download Sampleサンプルのダウンロード

バインド可能プロパティは、プロパティをフィールドでバッキングするのではなく、プロパティを BindableProperty 型でバッキングすることで CLR プロパティ機能を拡張します。 バインド可能なプロパティの目的は、親子リレーションシップを介して設定されたデータ バインディング、スタイル、テンプレート、値をサポートするプロパティ システムを提供することです。 さらに、バインド可能なプロパティは、既定値、プロパティ値の検証、プロパティの変更を監視するコールバックを提供できます。

プロパティは、次の 1 つ以上の機能をサポートするために、バインド可能プロパティとして実装する必要があります。

  • データ バインディングに有効なターゲット プロパティとしての動作。
  • スタイルを使用してプロパティを設定する。
  • プロパティの型の既定値とは異なる既定のプロパティ値を指定します。
  • プロパティの値の検証。
  • プロパティの変更の監視。

Xamarin.Forms バインド可能プロパティの例には、Label.TextButton.BorderRadiusStackLayout.Orientation があります。 各バインド可能なプロパティには、同じクラスで公開され、バインド可能なプロパティの識別子である BindableProperty 型の対応する public static readonly フィールドがあります。 たとえば、Label.Text プロパティに対応するバインド可能プロパティ識別子は Label.TextProperty です。

バインド可能なプロパティを作成する

バインド可能なプロパティを作成するプロセスは次のとおりです。

  1. BindableProperty.Create メソッドのオーバーロードのいずれかを使用して BindableProperty インスタンスを作成します。
  2. 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 メソッドを呼び出します。