MvxListView create binding for template layout from code

android, mvvmcross, xamarin.android

Solution

To do the bindings in code it's probably best to:

- implement a custom `MvxListViewItem`

- implement a custom `MvxAdapter` to return the custom list view item

- implement a custom `MvxListView` to use the custom `MvxAdapter`

Not tested, but the code for this is roughly:

1. implement a custom `MvxListViewItem`

public class CustomListItemView
    : MvxListItemView
{
    public MvxListItemView(Context context,
                           IMvxLayoutInflater layoutInflater,
                           object dataContext,
                           int templateId)
        : base(context, layoutInflater, dataContext, templateId)
    {
        var control = this.FindViewById<TextView>(Resource.Id.list_complex_title);
        var set = this.CreateBindingSet<CustomListViewItem, YourThing>();
        set.Bind(control).To(vm => vm.Title);
        set.Apply();
    }
}

2. Create a custom `MvxAdapter`

In this override `CreateBindableView`

public class CustomAdapter
    : MvxAdapter
{
    public CustomAdapter(Context context)
        : base(context)
    {
    }

    protected override IMvxListItemView CreateBindableView(object dataContext, int templateId)
    {
        return new CustomListItemView(_context, _bindingContext.LayoutInflater, dataContext, templateId);
    }
}

original: https://github.com/MvvmCross/MvvmCross/blob/v3.1/Cirrious/Cirrious.MvvmCross.Binding.Droid/Views/MvxAdapter.cs#L298

3. implement a custom `MvxListView` to use the adapter

This should be as simple as:

public class CustomListView
    : MvxListView
{
    public CustomListView(Context context, IAttributeSet attrs)
        : base(context, attrs, new CustomAdapter(context))
    {
    }
}

As long as this is in your main UI assembly, this should be useable in your axml as:

<CustomListView 
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    local:MvxBind="ItemsSource AutoListItems; ItemClick AutoListItemClicked"
    local:MvxItemTemplate="@layout/vbmvxautoviewlistitem" />

If `CustomListView` is not in your main UI assembly, then there are some tricks to get MvvmCross to pick it up during your `Setup` - see Providing Custom Android View Assemblies in https://github.com/MvvmCross/MvvmCross/wiki/Customising-using-App-and-Setup#wiki-providing-custom-views-android

The above is the best way to do this (IMO) - but if you wanted to, then you could do it in less code by just applying the bindings inside the custom adapter and by setting that adapter in `OnCreate` in your `Activity`

Problem

Lets say I have a simple Layout with a MvxListView: ``` <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:local="http://schemas.android.com/apk/res/LiivControl.Client.Droid" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent"> <Mvx.MvxListView android:layout_width="fill_parent" android:layout_height="wrap_content" local:MvxBind="ItemsSource AutoListItems; ItemClick AutoListItemClicked" local:MvxItemTemplate="@layout/vbmvxautoviewlistitem" /> </LinearLayout> ``` My item template layout is as follows: ``` <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:local="http://schemas.android.com/apk/res/LiivControl.Client.Droid" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" android:paddingTop="10dip" android:paddingBottom="10dip" android:paddingLeft="15dip"> <TextView android:id="@+id/list_complex_title" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" /> <TextView android:id="@+id/list_complex_caption" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceSmall" /> </LinearLayout> ``` I would like to specify the bindings for the two textview elements in my itemtemplate from code behind. I am not sure how best to go about it. I'm guess I could do something "OnViewModelSet" in the view code behind of the MvxListView. I have tried the following but for it obviously doesn't work because it can't find the control. ``` protected override void OnViewModelSet() { IVbMvxAutoListViewModel vm = base.ViewModel as IVbMvxAutoListViewModel; TextView title = this.FindViewById<TextView>(Resource.Id.list_complex_title); this.CreateBinding(title).For(x => x.Text).To(vm.ListItemDescriptor.TitlePropName).Apply(); TextView subTitle = this.FindViewById<TextView>(Resource.Id.list_complex_caption); this.CreateBinding(subTitle).For(x => x.Text).To(vm.ListItemDescriptor.SubTitlePropName).Apply(); base.OnViewModelSet(); } ``` My other thought was to somehow intercept the oncreate for the itemtemplate view but OnCreate doesn't get called if I create a view code file for my itemtemplate layout.

Original source