How to use the free MvvmCross API to bind a RecyclerView TextView element to a ViewModel property on Android?

I am using MvvmCross in a Xamarin Android project. I have an MvxActivity with an MvxRecyclerView that I have assigned an element template in its layout file.

 <MvxRecyclerView android:id="@+id/my_recycler_view" local:MvxItemTemplate="@layout/item_recycler_view" /> 

ViewModel is quite simple, it consists of only one property, which contains data for display in RecyclerView :

 public class MainViewModel : MvxViewModel { private IEnumerable<ViewModelItem> _viewModelItems; public IEnumerable<ViewModelItem> ViewModelItems { get { return _viewModelItems; } set { SetProperty(ref _viewModelItems, value); } } } 

Generally, I like to use the MvvmCross API as much as possible because of the implicit support for refactoring. Therefore, in my activity, I bind the MvxRecyclerView property as follows:

 var recyclerView = View.FindViewById<MvxRecyclerView>(Resource.Id.my_recycler_view); var set = this.CreateBindingSet<MainView, MainViewModel>(); set.Bind(recyclerView) .For(v => v.ItemsSource) .To(vm => vm.ViewModelItems); set.Apply(); 

So far so good. Now the layout file for the element template basically just contains a TextView :

 <LinearLayout> <TextView android:id="@+id/innerText" /> </LinearLayout> 

And my ViewModelItem class looks like this:

 public class ViewModelItem { public string Title { get; set; } } 

Now my question is: how and where do I bind the TextView.Text property to the ViewModelItem.Title property using the free API?

I know that it’s quite easy to do without a free API by providing the MvxBind attribute in the element template’s layout file, but I would prefer a free API solution.

+5
source share
2 answers

Inherit from the MvxRecyclerAdapter and create a custom adapter for your RecyclerView. Override OnCreateViewHolder and return custom ViewHolder.

 public class MyAdapter : MvxRecyclerAdapter { public MyAdapter(IMvxAndroidBindingContext bindingContext) : base(bindingContext) { } public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType) { var itemBindingContext = new MvxAndroidBindingContext(parent.Context, this.BindingContext.LayoutInflaterHolder); var view = this.InflateViewForHolder(parent, viewType, itemBindingContext); return new MyViewHolder(view, itemBindingContext); } } 

Inside this ViewHolder, you can use the Fluent API to bind.

 public class MyViewHolder : MvxRecyclerViewHolder { private readonly TextView textView; public MyViewHolder(View itemView, IMvxAndroidBindingContext context) : base(itemView, context) { this.textView = itemView.FindViewById<TextView>(Android.Resource.Id.Text1); this.DelayBind(() => { var set = this.CreateBindingSet<MyViewHolder, ViewModelItem>(); set.Bind(this.textView).To(x => x.Title); set.Apply(); }); } } 

In your activity, create an adapter and add it to your RecyclerView:

 var adapter = new MyAdapter((IMvxAndroidBindingContext)this.BindingContext); recyclerView.Adapter = adapter; 

and bind your items to the ItemsSource of your adapter:

 set.Bind(this.adapter).For(x => x.ItemsSource).To(x => x.ViewModelItems); 
+10
source

Based on Ken's answer, I created a couple of support classes and extensions to generalize the binding of elements and copy them along with a use case in github:

https://github.com/lauxjpn/MvxItemBinder

It allows you to write element bindings as shown below:

 var recyclerView = FindViewById<MvxRecyclerView>(Resource.Id.RecyclerView); var set = this.CreateBindingSet<MainActivity, MainViewModel>(); set.Bind(recyclerView) .For(v => v.ItemsSource) .To(vm => vm.Items); set.Apply(); recyclerView.BindItems<ItemViewModel>(this, (itemView, itemSet) => itemSet.Bind(itemView.FindViewById<TextView>(Resource.Id.item_template)) .For(v => v.Text) .To(vm => vm.Title) ); 

Or even shorter:

 var recyclerView = FindViewById<MvxRecyclerView>(Resource.Id.RecyclerView); var set = this.CreateBindingSet<MainActivity, MainViewModel>(); set.Bind(recyclerView.BindItems<ItemViewModel>(this, (itemView, itemSet) => itemSet.Bind(itemView.FindViewById<TextView>(Resource.Id.item_template)) .For(v => v.Text) .To(vm => vm.Title))) .For(v => v.ItemsSource) .To(vm => vm.Items); set.Apply(); 
+2
source

Source: https://habr.com/ru/post/1257971/


All Articles