少し前に Xamarin.Forms でもユーザーコントロール的な View が作成できることがわかったので、今度はこうしたカスタム View に BindableProperty を追加してみようと思います
本家 XAML の依存関係プロパティのようなものみたいですね
まずは ContentView で下記のような XAML を記述します
<?xml version="1.0" encoding="utf-8" ?> <ContentView xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" xmlns:vm="clr-namespace:XamarinSample.ViewModels;assembly=XamarinSample" x:Class="XamarinSample.Views.ArcStepper" Value="{Binding TimerValue}"> <ContentPage.BindingContext> <vm:TimerViewModel/> </ContentPage.BindingContext> <StackLayout Orientation="Vertical" VerticalOptions="Center" HorizontalOptions="Center" Spacing="10"> <Label x:Name="label" Font="30" HorizontalOptions="Center"/> <Stepper ValueChanged="OnValueChanged" HorizontalOptions="Center"/> </StackLayout> </ContentView>
スポンサードリンク
そしてコードビハインドを次のように記述
/// <summary> /// 円弧型 Stepper /// </summary> public partial class ArcStepper : ContentView { /// <summary> /// Value Bindable プロパティ /// </summary> public static readonly BindableProperty ValueProperty = BindableProperty.Create( "Value", // プロパティ名 typeof (TimeSpan), // プロパティの型 typeof (ArcStepper), // プロパティを持つ View の型 TimeSpan.FromMinutes(1), // 初期値 BindingMode.TwoWay, // バインド方向 null, // バリデーションメソッド ArcStepper.OnValuePropertyChanged, // 変更後イベントハンドラ null, // 変更時イベントハンドラ null); // BindableProperty.CoerceValueDelegate Xamarin 公式にも説明なしなので用途不明 /// <summary> /// Value CLR プロパティ /// </summary> public TimeSpan Value { get { return (TimeSpan) this.GetValue(ValueProperty); } set { this.SetValue(ValueProperty, value); } } /// <summary> /// コンストラクタ /// </summary> public ArcStepper() { this.InitializeComponent(); // 初期値を画面に反映させる OnValuePropertyChanged(this, null, this.Value); } /// <summary> /// Value 変更後イベントハンドラ /// </summary> /// <param name="bindable">BindableObject</param> /// <param name="oldValue">古い値</param> /// <param name="newValue">新しい値</param> private static void OnValuePropertyChanged(BindableObject bindable, object oldValue, object newValue) { var view = bindable as ArcStepper; if (view == null || !(newValue is TimeSpan)) { return; } view.label.Text = string.Format("{0:h\\:mm\\:ss}", (TimeSpan)newValue); } /// <summary> /// Stepper による値変更イベントハンドラ /// </summary> /// <param name="sender">イベント発行者</param> /// <param name="e">イベント引数</param> private void OnValueChanged(object sender, ValueChangedEventArgs e) { if (e.NewValue - e.OldValue > 0) { // 1分加算 this.Value = this.Value.Add(TimeSpan.FromMinutes(1)); } else { // 1分減算 this.Value = this.Value.Add(-TimeSpan.FromMinutes(1)); } } }
BindableProperty は Create メソッドで生成します
CoerceValueDelegate の用途は不明ですが他はひと通りのことができそうな引数がそろっている感じですね
上記のコードを実行すると・・・
ちゃんと表示されました(丸いライトグレイの円はシミュレータが表示してくれるタップ位置の目印みたいです)
デフォルト値は 1:00 ですが TimerViewModel.TimerValue に 5:00 が設定されているのできちんと反映されていますね
さらに Stepper の + ボタンをタップすると
画面も ViewModel もちゃんと 1:00 加算されました
バインド成功!