Edited at

Xamarin.Forms と ReactiveProperty で快適MVVM生活

More than 1 year has passed since last update.

 Xamarin.Forms は、Xamarin に新たに搭載されたクロスプラットフォームUIフレームワーク&MVVMフレームワークです。

 ReactiveProperty は、MVVMの(特に ViewModelの)実装を強力にサポートしてくれる、Reactive Extensions を基盤としたライブラリです。

両者を組み合わせると、Android/iOSアプリが COOL な感じで書けるんじゃないか、という事で試してみました。


0. 環境など

Mac + Xamarin Studio を使いますが、Windows + Visual Studio + Xamarin-Addin でもイケると思います。


1. 導入


プロジェクトの作成

新規ソリューションを、[C#]−[Mobile Apps]−[Blank App(Xamarin.Forms Portable)]で作成します。


PCL の Profile を変更

 作成されたソリューションの一番上にあるプロジェクト(.Android とか .iOS が付いていないやつ)のプロジェクト設定を開いて Profile を PCL 4.5 - Profile49 に変更します。元々の Profile78 では ReactiveProperty が Nuget からインストールできないためです。最近のプラットフォームを対象にするなら、あまり影響はなさそうです。


Nuget で Reactive Extensions と ReactiveProperty を追加

 メニューの[プロジェクト]ー[Add Packages]で Nuget のダイアログを開き、図のように 「Reactive Extensions - Main Library」と「ReactiveProperty Portable」を追加します。

(Reactive Extensions の追加の際、なにやらWarningが出るようですが、とりあえず進めます。)


2. ViewModel の実装

 PCL のプロジェクトに、FirstViewModel.cs を作成します。

 FirstViewModel は、以下のようなプロパティとコマンドを持ちます。


  • InputTextプロパティ : EditBox の入力に応じて更新

  • DisplayTextプロパティ : InputText の変化から1秒後に、InputText を大文字にして更新

  • Clearコマンド : InputText が 'clear' の時のみ有効。実行すると InputText を空にする。

これらの実装が下のようになります。


FirstViewModel.cs

using System;

using Codeplex.Reactive;
using System.Reactive.Linq;

namespace FormsWithRxProperty.ViewModels
{
public class FirstViewModel
{
private readonly ReactiveProperty<string> _inputText =
new ReactiveProperty<string>("Hoge");
public ReactiveProperty<string> InputText
{
get { return _inputText; }
}

public ReactiveProperty<string> DisplayText
{
get; private set;
}

public ReactiveCommand Clear
{
get; private set;
}

public FirstViewModel()
{
// DisplayText は、InputText の変更から1秒後に大文字にして更新
this.DisplayText = _inputText
.Delay(TimeSpan.FromSeconds(1))
.Select(x => x.ToUpper())
.ToReactiveProperty();

// InputText が `clear` の時に実行可能
this.Clear = _inputText
.Select(x => x.Equals("clear"))
.ToReactiveCommand();
// 実行されたら、InputText を空にする
this.Clear.Subscribe(_ => _inputText.Value = String.Empty);
}

}
}


 面倒な INotifyPropertyChanged の実装が必要なく、すっきりと記述できます。

 また、他のプロパティに関連して(反応して)値が変化するプロパティや、コマンドの利用可否などが、Reactive Extensions の機能により、流れるように記述できます。


3. 画面及び ViewModel との Binding の実装

 画面(UI)は、Xamarin.Forms の恩恵で、Android/iOS 共通で実装できます。XAML も使えますが、よく知らないのでコードでUIを記述します。

 PCL のプロジェクトに、 FirstPage.cs を作成し、以下のように実装します。


FirstPage.cs

using System;

using Xamarin.Forms;
using FormsWithRxProperty.ViewModels;

namespace FormsWithRxProperty.Pages
{
public class FirstPage : ContentPage
{
public FirstPage()
{
// UI
var entry = new Entry
{
Text = "Hello, Forms!",
VerticalOptions = LayoutOptions.Center,
HorizontalOptions = LayoutOptions.FillAndExpand,
};

var label = new Label
{
VerticalOptions = LayoutOptions.Center,
HorizontalOptions = LayoutOptions.CenterAndExpand,
};

var button = new Button
{
Text = "Clear (type 'clear' to enable)",
VerticalOptions = LayoutOptions.Center,
HorizontalOptions = LayoutOptions.FillAndExpand,
};

this.Content = new StackLayout
{
Padding = new Thickness(50f),
VerticalOptions = LayoutOptions.Start,
HorizontalOptions = LayoutOptions.Fill,
Orientation = StackOrientation.Vertical,
Children =
{
entry,
label,
button
}
};

// ViewModel との Binding
this.BindingContext = new FirstViewModel();
entry.SetBinding<FirstViewModel>(Entry.TextProperty, vm=>vm.InputText.Value);
label.SetBinding<FirstViewModel>(Label.TextProperty, vm=>vm.DisplayText.Value);
button.SetBinding<FirstViewModel>(Button.CommandProperty, vm=>vm.Clear);
}
}
}


 ちょっと長いですが、画面に「エディットボックス」「ラベル」「ボタン」が縦に並んでいるだけです。

 下部の4行で、FirstViewModel の各プロパティ、コマンドと Bind しています。

 もともとあった App.cs は、FirstPage を生成するだけにします。


App.cs

using System;

using Xamarin.Forms;
using FormsWithRxProperty.Pages;

namespace FormsWithRxProperty
{
public class App
{
public static Page GetMainPage()
{
return new FirstPage();
}
}
}



動かす!

.Android か .iOS の付いたプロジェクトをスタートアップにして、実行します。


追記 2014.9.10

実機で動作確認するの忘れてました(実機はAOTなのに対してiOSシミュレータはJITなのでリフレクションとかが普通に動いてしまう)。

実機でも問題なく動作しました!


追記 2014.9.11 INotifyPropertyChanged の利用

ViewModel は INotifyPropertyChanged を実装して作成するのが一般的です。既にそのようにして作られた ViewModel でも IObservable 化して、ReactiveProperty で利用できます。


SecondViewModel.cs

public class SecondViewModel : INotifyPropertyChanged

{
public ReactiveProperty<string> ValidationAttr { get; private set; }
public event PropertyChangedEventHandler PropertyChanged;

private string _myName = "HoGe";
public string MyName
{
get { return _myName; }
set
{
if (_myName == value) return;

_myName = value;
PropertyChanged(this, new PropertyChangedEventArgs("MyName"));
}
}

public ReactiveProperty<string> LowerText { get; private set; }

private ICommand _resetCommand;
public ICommand ResetCommand
{
get
{
return _resetCommand ?? (_resetCommand =
new Xamarin.Forms.Command(() => MyName = "XAAAAMAAARIN!!"));
}
}

public SecondViewModel()
{
this.LowerText = this.ObserveProperty(x => x.MyName)
.Select(x => x.ToLower())
.ToReactiveProperty();
}
}



まとめ

 Reactive Extensions のメリットを活かして MVVM を構築できる ReactiveProperty と、ワンソースで Android/iOS の画面を定義でき、さらに Binding までも共通にできる Xamarin.Forms の組み合わせは、今後のモバイルアプリケーション開発をとても効率的にしてくれます、 そしてなにより楽しい!

 今回のサンプルプログラムは

 に置きましたので、是非試してみてください。


ReactiveProperty


Xamarin.Forms