かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

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