基本的なMVVMとデータバインディングのサンプルはこちら
概要
XamarinのページのPart 5. From Data Bindings to MVVMのInteractive MVVMの章のRGB版です。
前の章に関しては一番上のリンクからどうぞ
R,G,Bのスライダーを操作するとLabelの色が変わるサンプルを作ります。
インタラクティブ(相互作用)というタイトルの通り、バインドの方向を双方向にします。
ViewModel
Viewモデルには、ラベルの色Colorインスタンスのプロパティと、double型のRed,Green,Blueプロパティを持つことにします。
プロパティの変更をViewに伝えるためにINotifyPropertyChangedインターフェイスを実装します。
namespace XamarinSample2
{
class RGBViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged = delegate { };
double red, green, blue;
Color color;
public double Red
{
set
{
if (red != value)
{
red = value;
PropertyChanged(this, new PropertyChangedEventArgs("Red"));
SetNewColor();
}
}
get { return red; }
}
public double Green
{
set
{
if (green != value)
{
green = value;
PropertyChanged(this, new PropertyChangedEventArgs("Green"));
SetNewColor();
}
}
get { return green; }
}
public double Blue
{
set
{
if (blue != value)
{
blue = value;
PropertyChanged(this, new PropertyChangedEventArgs("Blue"));
SetNewColor();
}
}
get { return blue; }
}
public Color Color
{
set
{
if (color != value)
{
color = value;
PropertyChanged(this, new PropertyChangedEventArgs("Color"));
Red = value.R;
Green = value.G;
Blue = value.B;
}
}
get { return color; }
}
public RGBViewModel()
{
this.Color = Color.White;
}
private void SetNewColor()
{
this.Color = Color.FromRgb(Red, Green, Blue);
}
}
}
R,G,Bのプロパティが変更されると、Colorを変更するSetNewColorを呼び出し、PropertyChangedイベントを発火させます。
逆にColorプロパティが変更された時も、R,G,Bを変更してPropertyChangedを発火します。
この時、Setアクセサでvalueが現在値と変わらない時は更新しないif文があることに注意してください。
これがないと、Redが変更される→Colorが変更される→Redを更新する→Colorが変更される……という無限ループになってしまいます。
View
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XamarinSample2;assembly=XamarinSample2"
x:Class="XamarinSample2.View.StartPage">
<StackLayout>
<StackLayout.BindingContext>
<local:RGBViewModel />
</StackLayout.BindingContext>
<Label x:Name="label"
Text="TEXT TEXT"
TextColor="{Binding Color}"
FontSize="Large"
HorizontalOptions="Center"
VerticalOptions="CenterAndExpand">
</Label>
<!-- RGB Slider -->
<Slider Value="{Binding Red,Mode=TwoWay}" />
<Slider Value="{Binding Green,Mode=TwoWay}" />
<Slider Value="{Binding Blue,Mode=TwoWay}" />
<!-- RGB Label -->
<StackLayout Orientation="Horizontal">
<Label Text="{Binding Red,StringFormat='{0:F2}'}"/>
<Label Text="{Binding Green,StringFormat='{0:F2}'}" />
<Label Text="{Binding Blue,StringFormat='{0:F2}'}" />
</StackLayout>
</StackLayout>
</ContentPage>
ContentPageの属性で、XML名前空間:localとして先ほどのViewModelのあるnamespaceを宣言しています。
そして、StackLayoutのBindingContextに先ほどのViewModelを設定しています。
BindingContextは、子要素にも適用されるので、StackLayoutの子要素でもバインドすることが出来ます。
LabelのTextColor属性をViewModelのColorプロパティとバインディングしているので、Labelのカラーが変わります。
スライダーに注目してください。Mode=TwoWayという文が書かれています。これはバインディングの方向を表していて、これによってスライダーの値とViewModelの色のプロパティを相互にバインドしています。
これがないとスライダーを動かしてもViewModelが変更されません。
一方その下に現在のR,G,Bを表すLabelがありますが、こちらはLabelの内容をViewModelに通知する必要が無いので、Modeを指定していません。
Modeには以下の様な種類があるようです(まだOneWayとTwoWayしか使ったことがない…)
Mode | 方向 |
---|---|
OneWay | ViewModel→View |
TwoWay | ViewModel↔View |
OneWayToSource | ViewModel←View |
OneTime | 最初の一度だけViewModelからViewへ |
まとめ
- BindingのModeをTwoWayに指定することで、相互作用するMVVMができる
- ViewModelのプロパティのSetアクセサは値が変更された時だけPropertyChangedイベントを発火する