I am using Xamarin Android with Reactive UI and Not using Xamarin Forms. I have a Custom ListView(I have defined it's layout as xaml). I have no idea how to bind this control into a observableCollection in ViewModel using OneWayBind method in the activity class.
I wrote it as,
this.OneWayBind(ViewModel, x => x.OutletListing, x => x.List).DisposeWith(SubscriptionDisposables);
But gives,
System.ArgumentException: Can't convert System.Collections.ObjectModel.ObservableCollection1 to Android.Widget.ListView. To fix this, register a IBindingTypeConverter
I saw in tutorials Xamarin Forms has used ItemSource property for this.
Could anyone please give a solution for this . thanks in advance.
Update I have no idea how to go ahead with the given answer. I want to figure this out more.
here is my ViewModel class.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using ReactiveUI;
using System.Collections.ObjectModel;
using System.Reactive.Linq;
using Splat;
using System.Reactive.Disposables;
using System.Threading.Tasks;
namespace DistributrIII.Mobile.Droid.ViewModels
{
public class StockTakeVM : ReactiveObject
{
protected Lazy<CompositeDisposable> ViewModelBindings = new Lazy<CompositeDisposable>(() => new CompositeDisposable());
public void RegisterObservables()
{
StockItemListing = new ReactiveList<StockItemListingResult>();
this.LoadStockItems = ReactiveCommand.CreateFromTask<FilterParams, List<StockItemListingResult>>(
async filter =>
{
System.Diagnostics.Debug.WriteLine("Load StockItemListing #1");
var r = await GetStockItemListing(filter);
return r;
},
Observable.Return(true))
.DisposeWith(ViewModelBindings.Value);
this.LoadStockItems.ThrownExceptions
.Subscribe(ex =>
{
System.Diagnostics.Debug.WriteLine("Load StockItemListing Failed");
});
this.LoadStockItems
.ObserveOn(RxApp.MainThreadScheduler)
.Subscribe(result =>
{
StockItemListing.Clear();
foreach (var item in result)
StockItemListing.Add(item);
});
}
async Task<List<StockItemListingResult>> GetStockItemListing(FilterParams filter)
{
List<StockItemListingResult> items = new List<StockItemListingResult>() {
new StockItemListingResult
{
StockItemName = "PORK SAUSAGES (ECONOMY) 500G",
StockItemCode = "CODE: J3103386",
StockItemAmt = "150"
},
new StockItemListingResult
{
StockItemName = "COLLAR BACON 500G",
StockItemCode = "CODE: J3155667",
StockItemAmt = "152"
},
new StockItemListingResult
{
StockItemName = "COLLAR BACON 1KG",
StockItemCode = "CODE: J2344545",
StockItemAmt = "200"
},
new StockItemListingResult
{
StockItemName = "PORK CHIPPOLATAS 1KG",
StockItemCode = "CODE: J31038779",
StockItemAmt = "378"
},
new StockItemListingResult
{
StockItemName = "PORK SAUSAGES (ECONOMY) 500G",
StockItemCode = "CODE: J3103386",
StockItemAmt = "23"
},
new StockItemListingResult
{
StockItemName = "PORK SAUSAGES (ECONOMY) 500G",
StockItemCode = "CODE: J3103386",
StockItemAmt = "454"
},
new StockItemListingResult
{
StockItemName = "COLLAR BACON 500G",
StockItemCode = "CODE: J3155667",
StockItemAmt = "123"
},
new StockItemListingResult
{
StockItemName = "COLLAR BACON 1KG",
StockItemCode = "CODE: J2344545",
StockItemAmt = "675"
},
new StockItemListingResult
{
StockItemName = "PORK CHIPPOLATAS 1KG",
StockItemCode = "CODE: J31038779",
StockItemAmt = "11"
},
new StockItemListingResult
{
StockItemName = "PORK SAUSAGES (ECONOMY) 500G",
StockItemCode = "CODE: J3103386",
StockItemAmt = "34"
}
};
return items;
}
// Observable Properties
ReactiveList<StockItemListingResult> _stockItemListing;
public ReactiveList<StockItemListingResult> StockItemListing
{
get { return _stockItemListing; }
private set { this.RaiseAndSetIfChanged(ref _stockItemListing, value); }
}
ReactiveCommand<FilterParams, List<StockItemListingResult>> _loadStockItems;
public ReactiveCommand<FilterParams, List<StockItemListingResult>> LoadStockItems
{
get { return _loadStockItems; }
private set { this.RaiseAndSetIfChanged(ref _loadStockItems, value); }
}
public StockTakeVM()
{
RegisterObservables();
}
}
public class StockItemListingResult : ReactiveObject
{
Guid _stockItemId;
public Guid Id
{
get { return _stockItemId; }
set { this.RaiseAndSetIfChanged(ref _stockItemId, value); }
}
string _stockItemName;
public string StockItemName
{
get { return _stockItemName; }
set { this.RaiseAndSetIfChanged(ref _stockItemName, value); }
}
string _stockItemCode;
public string StockItemCode
{
get { return _stockItemCode; }
set { this.RaiseAndSetIfChanged(ref _stockItemCode, value); }
}
string _stockItemAmt;
public string StockItemAmt
{
get { return _stockItemAmt; }
set { this.RaiseAndSetIfChanged(ref _stockItemAmt, value); }
}
}
}
And My Activity Class is as follows
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using ReactiveUI;
using Android.Support.V4.Widget;
using Android.Support.Design.Widget;
using DistributrIII.Mobile.Droid.ViewModels;
using DistributrIII.Mobile.Droid.Util;
using System.Reactive.Disposables;
using DistributrIII.Mobile.Droid.Models;
using Android.Support.V7.Widget;
using Android.Support.V7.App;
using Splat;
using Android.Support.V4.View;
using System.Reactive.Linq;
namespace DistributrIII.Mobile.Droid.Views.StockTake
{
[Activity(Label = "StockTakeActivity", MainLauncher = true, Theme = "@style/MainTheme")]
public class StockTakeActivity : DistributrBaseActivity<StockTakeVM>
{
private Android.Support.V7.Widget.SearchView _searchView;
public ListView List { get; private set; }
ReactiveList<StockItemListingResult> StockListItems;
StockTakeScreenAdapter osadapter;
List<StockItemModel> items = new List<StockItemModel>();
public StockTakeActivity()
{
this.ViewModel = new StockTakeVM();
this.StockListItems = new ReactiveList<StockItemListingResult>();
}
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.Activity_StockTake);
this.Bind(ViewModel, x => x.StockItemListing, x => x.StockListItems);
//checking whether change happens
// StockListItems.ItemChanged
// .Where(x => x.PropertyName == "StockItemAmt" && x.Sender.StockItemAmt)
// .Select(x => x.Sender)
// .Subscribe(x => {
// Console.WriteLine("Make sure to save {0}!", x.DocumentName);
//});
this.WhenAnyValue(view => view.ViewModel.StockItemListing)
.Where(listing => listing != null)
.Select(listing => new DistributrIII.Mobile.Droid.Util.ReactiveListAdapter<StockTakeVM>(listing, Resource.Layout.Activity_StockTake))
.BindTo(this, view => view.List.Adapter);
this.ViewModel.LoadStockItems.Execute(new FilterParams
{
NameFilter = "",
Status = "All"
}).Subscribe();
//List = FindViewById<ListView>(Resource.Id.List);
SetupReactiveLists(this);
var toolbarST = FindViewById<Android.Support.V7.Widget.Toolbar>(Resource.Id.toolbarST);
toolbarST.InflateMenu(Resource.Menu.StockTakeSearch);
toolbarST.Title = "Stock Take";
}
public void SetupReactiveLists(Activity context)
{
List = FindViewById<ListView>(Resource.Id.List);
foreach (var item in StockListItems)
{
StockItemModel stockitem = new StockItemModel
{
StockItemName = item.StockItemName,
StockItemCode = item.StockItemCode
};
items.Add(stockitem);
}
osadapter = new StockTakeScreenAdapter(this, items);
List.Adapter = osadapter;
List.ItemClick += OnListItemClick;
}
}
here is my Listadapter class.
public class StockTakeScreenAdapter : BaseAdapter<StockItemModel>
{
List<StockItemModel> items;
Activity context;
public StockTakeScreenAdapter(Activity context, List<StockItemModel> items) : base()
{
this.context = context;
this.items = items;
}
public override long GetItemId(int position)
{
return position;
}
public override StockItemModel this[int position]
{
get { return items[position]; }
}
public override int Count
{
get { return items.Count; }
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
var item = items[position];
View view = convertView;
if (view == null)
view = context.LayoutInflater.Inflate(Resource.Layout.ViewCell_StockTake, null);
view.FindViewById<TextView>(Resource.Id.stockitem_name).Text = item.StockItemName;
view.FindViewById<TextView>(Resource.Id.stockitem_code).Text = item.StockItemCode;
view.FindViewById<TextView>(Resource.Id.stockitem_amt).Text = item.StockItemAmt;
return view;
} }
Here are my questions
- Should I want to create another reactive List in the Activity Class in order to bind.
- If I changed the value of a list item , does it update the list (I expect to update the value of a list item using another fragment class.)
I am new to android development with RX. Pardon me if I mess up the things. Thanks again.