Search code examples
c#androidxamarinmvvmcrossmvxbind

How do I bind to `ItemClick` for a `MvxLinearLayout`?


I have a ScrollView which originally wrapped two MvxListView controls.

Having ListView controls in a ScrollView isn't supported by Android though, which makes sense, because they both try to fill the parent height and provide their own scrolling logic.

What I want is two unscrollable lists with their full height inside my ScrollView. ListView which MvxListView extends doesn't support this without hacking the height manually.

The reason I want this is because I have two separate lists that I have bound to separate sources and they both have their own header. I need all of this to be scrollable within one ScrollView.

Then I found MvxLinearLayout which is a bindable LinearLayout which has an ItemSource property I can bind to. It works excellent, it shows my items and get the full height of all items so I can scroll both my lists in my ScrollView. The problem is that it doesn't seem to have an ItemClick property, so I don't have a way to get user input from my list.

Does anyone know a clean way of doing this in a bindable manner? I don't want to attach onItemClick handlers in my code behind. Is there another MvvmCross control that can do what I want?


Solution

  • You can extend MvxLinearLayout to support ItemClick:

    public class MvxClickableLinearLayout : MvxLinearLayout
    {
        public MvxClickableLinearLayout(Context context, IAttributeSet attrs)
            : this(context, attrs, new MvxClickableLinearLayoutAdapter(context))
        {
        }
    
        public MvxClickableLinearLayout(Context context, IAttributeSet attrs, MvxClickableLinearLayoutAdapter adapter)
            : base(context, attrs, adapter)
        {
            var mvxClickableLinearLayoutAdapter = Adapter as MvxClickableLinearLayoutAdapter;
            if (mvxClickableLinearLayoutAdapter != null)
            {
                mvxClickableLinearLayoutAdapter.OnItemClick = OnItemClick;
            }
        }
    
        public ICommand ItemClick { get; set; }
    
        public void OnItemClick(object item)
        {
            if (ItemClick != null && ItemClick.CanExecute(item))
            {
                ItemClick.Execute(item);
            }
        }
    }
    

    Adapter:

    public class MvxClickableLinearLayoutAdapter : MvxAdapterWithChangedEvent, View.IOnClickListener
    {
        public delegate void ItemClickDelegate(object item);
    
        public ItemClickDelegate OnItemClick;
    
        public MvxClickableLinearLayoutAdapter(Context context)
            : base(context)
        {
        }
    
        public void OnClick(View view)
        {
            var mvxDataConsumer = view as IMvxDataConsumer;
    
            if (mvxDataConsumer != null && OnItemClick != null)
            {
                OnItemClick(mvxDataConsumer.DataContext);
            }
        }
    
        protected override View GetView(int position, View convertView, ViewGroup parent, int templateId)
        {
            View view = base.GetView(position, convertView, parent, templateId);
            view.SetOnClickListener(this);
            return view;
        }
    }
    

    Now you can bind to ItemClick just like you would do with a ListView:

    local:MvxBind="ItemClick SomeCommand"