特にReactivePropertyでサポートはしてないので自前でやるっきゃないです!ということでこういうクラスを書いてみました。
ReadOnlyReactiveCollection
public static class ReadOnlyCollectionExtensions { /// <summary> /// ReadOnlyReactiveCollectionをIListAdapterに変換する /// </summary> /// <typeparam name="T"></typeparam> /// <param name="self"></param> /// <param name="createRowView">行のデータを表示するためのViewを作る処理</param> /// <param name="setRowData">行にデータを設定する処理</param> /// <returns></returns> public static IListAdapter ToAdapter<T>(this ReadOnlyReactiveCollection<T> self, Func<View> createRowView, Action<T, View> setRowData) { return new ReadOnlyReactiveCollectionAdapter<T>(self, createRowView, setRowData); } } /// <summary> /// ReadOnlyReactiveCollection用のAdapterクラス /// </summary> /// <typeparam name="T"></typeparam> class ReadOnlyReactiveCollectionAdapter<T> : BaseAdapter<T> { // もとになるコレクション private ReadOnlyReactiveCollection<T> source; // 行のデータを表示するためのViewを作る処理 private Func<View> createRowView; // 行にデータを設定する処理 private Action<T, View> setRowData; public ReadOnlyReactiveCollectionAdapter( ReadOnlyReactiveCollection<T> source, Func<View> createRowView, Action<T, View> setRowData) { this.source = source; this.createRowView = createRowView; this.setRowData = setRowData; } public override T this[int position] { get { return source[position]; } } public override int Count { get { return this.source.Count; } } public override long GetItemId(int position) { return position; } public override View GetView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = this.createRowView(); } this.setRowData(this[position], convertView); return convertView; } }
使い方は簡単です。以下のようなコマンドを実行するとコレクションにデータを追加するだけのViewModelがあったとします。
public class MainActivityViewModel { private ObservableCollection<string> source = new ObservableCollection<string> { "a", "b", "c" }; public ReadOnlyReactiveCollection<string> Items { get; private set; } public ReactiveCommand AddItemCommand { get; private set; } public MainActivityViewModel() { this.Items = source.ToReadOnlyReactiveCollection(); this.AddItemCommand = new ReactiveCommand(); this.AddItemCommand.Subscribe(_ => { this.source.Add("item" + DateTime.Now); }); } }
そして、ボタンとListViewを置いた画面でさくっと紐づけ。
[Activity(Label = "App1", MainLauncher = true, Icon = "@drawable/icon")] public class MainActivity : Activity { private MainActivityViewModel viewModel = new MainActivityViewModel(); protected override void OnCreate(Bundle bundle) { base.OnCreate(bundle); // Set our view from the "main" layout resource SetContentView(Resource.Layout.Main); this.FindViewById<Button>(Resource.Id.button1).Click += viewModel.AddItemCommand.ToEventHandler(); var listView = this.FindViewById<ListView>(Resource.Id.listView1); listView.Adapter = viewModel.Items.ToAdapter( () => LayoutInflater.FromContext(this).Inflate(Resource.Layout.layout1, null), (x, v) => v.FindViewById<TextView>(Resource.Id.textView1).Text = x); this.viewModel.Items.CollectionChangedAsObservable().Subscribe(_ => listView.InvalidateViews()); } }
一応レイアウトファイルも
main.axml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <Button android:text="Button" android:layout_width="match_parent" android:layout_height="wrap_content" android:id="@+id/button1" /> <ListView android:minWidth="25px" android:minHeight="25px" android:layout_width="match_parent" android:layout_height="match_parent" android:id="@+id/listView1" /> </LinearLayout>
layout1.axml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent"> <ImageView android:src="@android:drawable/ic_menu_gallery" android:layout_width="wrap_content" android:layout_height="match_parent" android:id="@+id/imageView1" /> <TextView android:text="Medium Text" android:textAppearance="?android:attr/textAppearanceMedium" android:layout_width="wrap_content" android:layout_height="match_parent" android:id="@+id/textView1" /> </LinearLayout>