特にReactivePropertyでサポートはしてないので自前でやるっきゃないです!ということでこういうクラスを書いてみました。
ReadOnlyReactiveCollection
publicstaticclass ReadOnlyCollectionExtensions { /// <summary>/// ReadOnlyReactiveCollectionをIListAdapterに変換する/// </summary>/// <typeparam name="T"></typeparam>/// <paramname="self"></param>/// <paramname="createRowView">行のデータを表示するためのViewを作る処理</param>/// <paramname="setRowData">行にデータを設定する処理</param>/// <returns></returns>publicstatic IListAdapter ToAdapter<T>(this ReadOnlyReactiveCollection<T> self, Func<View> createRowView, Action<T, View> setRowData) { returnnew 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; } publicoverride T this[int position] { get { return source[position]; } } publicoverrideint Count { get { returnthis.source.Count; } } publicoverridelong GetItemId(int position) { return position; } publicoverride View GetView(int position, View convertView, ViewGroup parent) { if (convertView == null) { convertView = this.createRowView(); } this.setRowData(this[position], convertView); return convertView; } }
使い方は簡単です。以下のようなコマンドを実行するとコレクションにデータを追加するだけのViewModelがあったとします。
publicclass 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")] publicclass MainActivity : Activity { private MainActivityViewModel viewModel = new MainActivityViewModel(); protectedoverridevoid 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"><Buttonandroid:text="Button"android:layout_width="match_parent"android:layout_height="wrap_content"android:id="@+id/button1" /><ListViewandroid: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"><ImageViewandroid:src="@android:drawable/ic_menu_gallery"android:layout_width="wrap_content"android:layout_height="match_parent"android:id="@+id/imageView1" /><TextViewandroid:text="Medium Text"android:textAppearance="?android:attr/textAppearanceMedium"android:layout_width="wrap_content"android:layout_height="match_parent"android:id="@+id/textView1" /></LinearLayout>