やること
こんな感じに、RGBのスライダーを動かすと色が変わる、みたいなアプリを作ってみる。
普通に作るとNotifyPropertyChanged
を駆使して値の変化を各プロパティに伝播させる必要があると思うのですが、ReactivePropertyを利用するとスゲー簡単に書けるよ、というサンプル。
View (XAML + C#)
using System;
using System.Collections.Generic;
using ReactiveColorChooser.ViewModels;
using Xamarin.Forms;
namespace ReactiveColorChooser.Views
{
public partial class ColorChooserPage : ContentPage
{
public ColorChooserPage()
{
InitializeComponent();
Title = "Reactive Color Chooser";
BindingContext = new ColorChooserViewModel();
Red.SetBinding<ColorChooserViewModel>(Slider.ValueProperty, vm => vm.Red.Value);
Green.SetBinding<ColorChooserViewModel>(Slider.ValueProperty, vm => vm.Green.Value);
Blue.SetBinding<ColorChooserViewModel>(Slider.ValueProperty, vm => vm.Blue.Value);
MixedColor.SetBinding<ColorChooserViewModel>(BoxView.BackgroundColorProperty, vm => vm.MixedColor.Value);
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" x:Class="ReactiveColorChooser.Views.ColorChooserPage">
<ContentPage.Content>
<StackLayout Padding="10, 20">
<StackLayout>
<Label Text="{Binding IntRed.Value}" TextColor="Red" />
<Slider x:Name="Red" Maximum="1" Minimum="0" Value="0" />
<Label Text="{Binding IntGreen.Value}" TextColor="Green" />
<Slider x:Name="Green" Maximum="1" Minimum="0" Value="0" />
<Label Text="{Binding IntBlue.Value}" TextColor="Blue" />
<Slider x:Name="Blue" Maximum="1" Minimum="0" Value="0" />
</StackLayout>
<StackLayout Orientation="Vertical" Padding="0, 20, 0, 0">
<BoxView x:Name="MixedColor" />
<Label Text="{Binding HexColor.Value}" TextColor="{Binding MixedColor.Value}" FontSize="20" />
</StackLayout>
</StackLayout>
</ContentPage.Content>
</ContentPage>
- ContentPageにBindingContextを設定することで、XAML側でBindingが使えるようになる。
- コード側でViewModelの値とViewの値をSetBindingで紐付けてあげる必要がある。一方で、SetBindingがずらずら並ぶのは、いささか冗長な感もある。
ViewModel
using System;
using System.Reactive.Linq;
using Reactive.Bindings;
using Xamarin.Forms;
namespace ReactiveColorChooser.ViewModels
{
public class ColorChooserViewModel
{
public ReactiveProperty<double> Red { get; private set; }
public ReactiveProperty<int> IntRed { get; private set; }
public ReactiveProperty<double> Green { get; private set; }
public ReactiveProperty<int> IntGreen { get; private set; }
public ReactiveProperty<double> Blue { get; private set; }
public ReactiveProperty<int> IntBlue { get; private set; }
public ReactiveProperty<Color> MixedColor { get; private set; }
public ReactiveProperty<string> HexColor { get; private set; }
public ColorChooserViewModel()
{
Red = new ReactiveProperty<double>(0.0f);
Green = new ReactiveProperty<double>(0.0f);
Blue = new ReactiveProperty<double>(0.0f);
// スライダーの値を0 - 255の範囲の整数に変換
IntRed = Observable.Select(Red, DoubleToInt).ToReactiveProperty();
IntGreen = Observable.Select(Green, DoubleToInt).ToReactiveProperty();
IntBlue = Observable.Select(Blue, DoubleToInt).ToReactiveProperty();
// スライダーの値から色を生成
MixedColor = Observable.CombineLatest(IntRed, IntGreen, IntBlue, (r, g, b) => Color.FromRgb(r, g, b)).ToReactiveProperty();
// 現在の色を16進数表示
HexColor = Observable.CombineLatest(IntRed, IntGreen, IntBlue, (r, g, b) => string.Format(@"#{0}{1}{2}", r.ToString("X2"), g.ToString("X2"), b.ToString("X2"))).ToReactiveProperty();
}
private int DoubleToInt(double x)
{
return (int)(x * 255);
}
}
}
-
ReactiveProperty<T>
型で宣言することで、勝手に値の変化を監視してくれます。イケてる! - 値の変化はObservableで受け取って処理すれば良いです。リアクティブ感!
- 処理後は
ToReactiveProperty
でReactiveProperty型に戻すのを忘れずに。
受け取った値に対してはいろいろな処理ができるので、こちらの早見表を参考にしながら書くと良いのではと思います。とても参考にさせていただきました、ありがとうございます。
Reactive Extensions再入門 番外編「System.Reactive.Linq.Observableのメソッド一覧とメモ」
C#の初心者的には、「ちゃんと変換されてるか分からんなー」というときはDo
を挟めば良いことだけ覚えていれば応用が効く気がしました。Rubyで言うtap
みたいなもんですね。
// 例: 値を変換するたびにログ表示
IntRed = Observable.Select(Red, DoubleToInt).Do(x => System.Diagnostics.Debug.WriteLine(x)).ToReactiveProperty();
あとがき
リアクティブ、良い!