5

我正在使用MvvmCross在我的Xamarin Android项目。我有一个MvxActivityMvxRecyclerView,我已经在其布局文件中分配了一个项目模板。如何使用MvvmCross fluent API将RecyclerView项目的TextView绑定到Android上其ViewModel的属性?

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

视图模型很简单,它是由刚刚保存数据的RecyclerView显示一个属性:

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

一般来说,我喜欢用MvvmCross流利的API尽可能因为隐式重构支持。 所以在我的活动,我绑定了MvxRecyclerView的属性是这样的:

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(); 

到目前为止好。现在,该项目模板布局文件基本上只包含一个TextView

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

而且我ViewModelItem类看起来是这样的:

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

我现在的问题是,如何和我在哪里绑定TextView.Text财产使用流利的API的ViewModelItem.Title财产?

我知道,通过在项目模板布局文件中提供MvxBind属性,没有流利的API很容易,但我更喜欢流利的API解决方案。

回答

10

从MvxRecyclerAdapter继承并为您的RecyclerView创建一个自定义适配器。覆盖OnCreateViewHolder并返回一个自定义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); 
    } 
} 

在此ViewHolder中,您可以使用Fluent API进行绑定。

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(); 
     }); 
    } 
} 

在你的活动创建适配器并将其添加到您的RecyclerView:

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

并结合您的项目到您的适配器的的ItemsSource:

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

有一点要注意,你可能想通过分配点击命令到您的自定义'ViewHolder',否则任何绑定到ItemClick都不会有任何影响。在[这个stackoverflow答案]示例(http://stackoverflow.com/questions/42938112/mvxrecyclerview-fluent-api-binding#answer-43055796)。 – Plac3Hold3r

2

基于肯的回答,我创建了几个支持类和扩展来概括项目绑定,并将它们与使用示例一起推送到github:

https://github.com/lauxjpn/MvxItemBinder

它可以让你写项目绑定类似如下:

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) 
); 

或者更短:

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(); 
相关问题