MvxRecyclerView Fluent API binding
Asked Answered
U

2

2

I am unable to bind ItemClick from MvxRecyclerView (or its Adapter) to a command on my ViewModel using Fluent API. It works if I put both ItemsSource and ItemClick in the XML so I am not interested in such solution.

I used this post as an excellent guideline (How to use the MvvmCross fluent API to bind a RecyclerView item's TextView to a property of its ViewModel on Android?) and all of that works except that I am unable to bind ItemClick on MvxRecyclerView (or the adapter) to a MainViewModel's command which will take me to the next fragment (ItemsSource works like a charm but its a property and not a command!).

For the sake of brevity, I will not be copying the code from the original post (How to use the MvvmCross fluent API to bind a RecyclerView item's TextView to a property of its ViewModel on Android?) so assume that the MainViewModel from that post has been enhanced with a command ShowItemCommand as such:

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

    public MvxCommand<ViewModelItem> ShowItemCommand
    {
        get
        {
            return new MvxCommand<ViewModelItem>(selectedItem =>
            {
                ShowViewModel<ViewModelItem>
                (new { itemId = selectedItem.Id });
            });
        }
    }
}

and everything else has been implemented as per the referenced post.

So now, in addition to ItemsSource, I want to wire up ItemClick on the MvxRecyclerView (or the Adapter) to the command. The reason these are interchangeable is that MvxRecyclerView just relays these commands to the Adapter.

Apparently, this should work...but it does not:

adapter.ItemClick = ViewModel.ShowItemCommand;

This does not work either:

set.Bind(recyclerView).For(v => v.ItemClick).To(vm => vm.ShowItemCommand);
Unfailing answered 21/3, 2017 at 21:5 Comment(0)
C
3

When creating a custom MvxRecyclerViewHolder you need to make sure that you assign the Click command over to the ViewHolder. This is done in the OnCreateViewHolder override of your custom adapter.


Example for 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)
        {
            Click = ItemClick,
            LongClick = ItemLongClick
        };
    }
}
Callaghan answered 27/3, 2017 at 20:22 Comment(0)
B
0

I can't reproduce your issue. I just created a new project, added a RecyclerView and added the following binding:

var set = this.CreateBindingSet<FirstView, FirstViewModel>();
set.Bind(recyclerView).For(v => v.ItemsSource).To(vm => vm.ViewModelItems);
set.Bind(recyclerView).For(v => v.ItemClick).To(vm => vm.ShowItemCommand);
set.Apply();

This works just as expected, where ItemClick triggers the ShowItemCommand. VM's look as follows:

public class ViewModelItem : MvxViewModel
{
    public void Init(string itemId)
    {
        Mvx.Trace($"Showing {itemId}");
    }

    public string Id { get; set; }
}


public class FirstViewModel
    : MvxViewModel
{
    public FirstViewModel()
    {
        ViewModelItems = new ViewModelItem[] {
            new ViewModelItem { Id = "Hello"},
            new ViewModelItem { Id = "World"},
            new ViewModelItem { Id = "Foo"},
            new ViewModelItem { Id = "Bar"},
            new ViewModelItem { Id = "Baz"}
        };
    }

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

    public MvxCommand<ViewModelItem> ShowItemCommand =>
        new MvxCommand<ViewModelItem>(DoShowItem);

    private void DoShowItem(ViewModelItem item)
    {
        ShowViewModel<ViewModelItem>(new { itemId = item.Id });
    }
}
Boman answered 21/3, 2017 at 22:6 Comment(2)
I agree with you. My initial implementation only had a MvxRecyclerView and bound to ItemsSource and ItemClick in XML. And that worked great. Replacing these bindings with Fluent API, works great. But I went further and (as per referenced note) rolled my own MvxRecyclerAdapter and MvxRecyclerViewHolder. In that implementation, ItemsSource works but ItemClick does not. Ultimately, I am aiming to get access to MvxViewViewHolder in order to implement highlighting/multi-choice for Contextual Action Bar.Unfailing
@Igor, you need to make sure you are assigning the click command over for your adapter to the your custom ViewHolder, see my answer for implementation example.Callaghan

© 2022 - 2024 — McMap. All rights reserved.