読者です 読者をやめる 読者になる 読者になる

かずきのBlog@hatena

日本マイクロソフトに勤めています。XAML + C#の組み合わせをメインに、たまにASP.NETやJavaなどの.NET系以外のことも書いています。掲載内容は個人の見解であり、所属する企業を代表するものではありません。

XamarinのAndroidアプリでReactivePropertyを使う 2016年版

随分昔に書いてますね。

blog.okazuki.jp

最近は事情も変わったので改めて書いてみようと思います。

ReactivePropertyは?

Reactive ExtensionsをベースにしたMVVMの支援ライブラリです。

blog.okazuki.jp

Xamarinでも使えるの?

Xamarin.AndroidとXamarin.iOSとXamarin.Formsに対応しています!ここでは、Xamarin.Androidでの使い方を紹介したいと思います(Mac持ってない)。因みに、ここで紹介するのと同じ感覚でiOSアプリでも使えるので興味を持った人は試してみてください!

Hello world的なもの

新規作成したときに作成されるカウントアップアプリをReactivePropertyを使って再現したみたいと思います。

プロジェクトの作成

Blank App(Android)を作成します。そして、NuGetからReactivePropertyを参照に追加します。

Counterクラスの作成

カウンターのクラスを作ります。普通のC#のクラスです。

using System.ComponentModel;

namespace CounterApp
{
    public class Counter : INotifyPropertyChanged
    {
        public event PropertyChangedEventHandler PropertyChanged;

        private int value;

        public int Value
        {
            get { return this.value; }
            private set
            {
                this.value = value;
                this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.Value)));
            }
        }

        public void Increment()
        {
            this.Value++;
        }
    }
}

ViewModelの作成

次にMainActivityのViewModelになるMainActivityViewModelクラスを作成します。

using Reactive.Bindings;
using Reactive.Bindings.Extensions;
using System;
using System.Linq;
using System.Reactive.Linq;

namespace CounterApp
{
    public class MainActivityViewModel
    {
        // Model
        private Counter Model { get; } = new Counter();

        // Viewへ公開するインターフェース
        public ReadOnlyReactiveProperty<string> CounterValue { get; }

        public ReactiveCommand IncremantCommand { get; }

        public MainActivityViewModel()
        {
            // Counter.Valueを監視して文字列化
            this.CounterValue = this.Model
                .ObserveProperty(x => x.Value)
                .Select(x => x.ToString())
                .ToReadOnlyReactiveProperty();

            // インクリメントするコマンド
            this.IncremantCommand = new ReactiveCommand();
            this.IncremantCommand.Subscribe(_ => this.Model.Increment());
        }
    }
}

ここまでは、普通のC#とReactivePropertyを使ったプログラミングです。

Vの作成

次にMainActivityに行きます。ReactivePropertyではAndroidのViewとバインドするための拡張メソッドとしてSetBindingというメソッドを提供しています。これを使うとコントロールのプロパティとReactivePropertyをバインドすることが出来ます。

また、コントロールのイベントをIObservableに変換したあとSetCommand拡張メソッドでコマンドを設定することで、コントロールのイベントとCommandを紐づけることが出来ます。

コードを書いてみましょう。

using Android.App;
using Android.OS;
using Android.Widget;
using Reactive.Bindings;
using System.Reactive.Linq;

namespace CounterApp
{
    [Activity(Label = "CounterApp", MainLauncher = true, Icon = "@drawable/icon")]
    public class MainActivity : Activity
    {
        private MainActivityViewModel ViewModel { get; } = new MainActivityViewModel();

        protected override void OnCreate(Bundle bundle)
        {
            base.OnCreate(bundle);

            // Set our view from the "main" layout resource
            SetContentView(Resource.Layout.Main);

            // Get our button from the layout resource,
            // and attach an event to it
            var button = FindViewById<Button>(Resource.Id.MyButton);

            // ButtonのTextとバインド
            button.SetBinding(
                x => x.Text,
                this.ViewModel.CounterValue);

            // ButtonのClickイベントとバインド
            Observable.FromEventPattern(button, nameof(Button.Click))
                .SetCommand(this.ViewModel.IncremantCommand);
        }
    }
}

axmlに書いてバインドはできないのでごめんなさい。でもコードでも割と直感的に書けるかなと思ってます。

実行すると

以下のようになります。

f:id:okazuki:20160406203357p:plain

気になった人は

因みに公式でしっかり書いてるのはこちら。 コレクションのバインドもサポートしています。

github.com

Xamarin.Formsで使ってくれてる人もいる

ちゃんとXamarin.Formsでも使えるよっていうことで!

tamafuyou.hatenablog.com