Jim Wilcox
Jim Wilcox

Reputation: 1573

MvxListView with MvxSpinner showing null entry on the first Item

I am using an MvxListView which contains and MvxSpinner. When my app runs, the trace shows several instances of:

Null values not permitted in spinner SelectedItem binding currently

I know for a fact that the entries on the data object are not null. Here is the pertinent code: The layout for the MvxListView is

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">
            <MvxListView
                android:layout_width="fill_parent"
                android:layout_height="fill_parent"
                local:MvxBind="ItemsSource ShipmentLots.Lots"
                local:MvxItemTemplate="@layout/inventorylotview" />
            <ImageButton
                android:src="@drawable/ic_action_new"
                android:layout_width="60dp"
                android:layout_height="match_parent"
                android:layout_gravity="center"
                local:MvxBind="Click NewLot_Clicked"
                android:id="@+id/btnLotNew" />
        </LinearLayout>

The layout for the MvxItemTemplate is as follows:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:local="http://schemas.android.com/apk/res-auto"
    android:orientation="horizontal"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <MvxSpinner
        android:layout_width="130dp"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        style="@style/InputSpinner"
        local:MvxItemTemplate="@layout/itemspinner"
        local:MvxDropDownItemTemplate="@layout/itemspinnerdropdown"
        local:MvxBind="ItemsSource LotColors; SelectedItem LotColor"
        android:id="@+id/spinner1" />
    <EditText
        android:layout_width="150dp"
        android:layout_height="wrap_content"
        style="@style/InputEditText"
        local:MvxBind="Text LotNo" />
    <ImageButton
        android:src="@drawable/ic_action_delete"
        android:layout_width="60dp"
        android:layout_height="match_parent"
        android:layout_gravity="center"
        local:MvxBind="Click DeleteClicked"
        android:id="@+id/btnLotDelete" />
</LinearLayout>

The InventoryViewModel is as follows:

public class InventoryViewModel
  : MvxViewModel
{
    public async void Init(Guid ID)
    {
        await MPS_Mobile_Driver.Droid.DataModel.ShipmentDataSource.GetShipmentInventory(ID);
        ShipmentInventory = ShipmentDataSource.CurrInventory;

        Shipment = await MPS_Mobile_Driver.Droid.DataModel.ShipmentDataSource.GetShipment((int)ShipmentInventory.idno, (short)ShipmentInventory.idsub);
        ShipmentLots = await MPS_Mobile_Driver.Droid.DataModel.ShipmentDataSource.GetShipmentLotList(Shipment.idno, Shipment.idsub);
    }

    private Shipment _Shipment;
    public Shipment Shipment
    {
        get { return _Shipment; }
        set { _Shipment = value; RaisePropertyChanged(() => Shipment); }
    }

    private ShipmentInventory _ShipmentInventory;
    public ShipmentInventory ShipmentInventory
    {
        get { return _ShipmentInventory; }
        set { _ShipmentInventory = value; RaisePropertyChanged(() => ShipmentInventory); }
    }

    private ShipmentLotList _ShipmentLots;
    public ShipmentLotList ShipmentLots
    {
        get { return _ShipmentLots; }
        set { _ShipmentLots = value; RaisePropertyChanged(() => ShipmentLots); }
    }

    public IMvxCommand NewLot_Clicked
    {
        get
        {
            return new MvxCommand(() => NewLot());
        }
    }

    private void NewLot()
    {
        ShipmentLot Lot = new ShipmentLot();
        Lot.ID = Guid.NewGuid();
        Lot.idno = Shipment.idno;
        Lot.idsub = Shipment.idsub;
        ShipmentLots.Lots.Add(Lot);
    }

}

The viewmodel for the ShipmentLots contains an observablecollection of Type ShipmentLot called Lots. The Class for ShipmentLots gets created from a WCF service. I extended it as follows:

public partial class ShipmentLot
{

    private static string[] _LotColors = { "Yellow", "Brown", "White", "Blue", "Orange", "Red", "Green", "Purple" };
    public string[] LotColors
    {
        get { return _LotColors; }
    }

    public IMvxCommand DeleteClicked
    {
        get
        {
            return new MvxCommand(() => DeleteLot());
        }
    }

    private void DeleteLot()
    {
        MPS_Mobile_Driver.Droid.Views.InventoryView act = (MiscFunctions.CurrActivity as MPS_Mobile_Driver.Droid.Views.InventoryView) ?? null;
        if (act != null)
        {
            act.DeleteLot(this);
        }
    }
}

That takes care of having the delete button work and supplying a color list for the MvxSpinner. When I run the app, I get the Null value not allowed error and the first item in the MvxListView has the wrong color on the MvxSpinner. THe subsequent ones work fine. I am not sure what is different on the first one. Anyone got any ideas on this?

Thanks, Jim

Upvotes: 1

Views: 981

Answers (1)

Jim Wilcox
Jim Wilcox

Reputation: 1573

After much help from @Cheesebaron and @Stuart I have found that if you use MvxSpinner or MvxAutoComplete in an ItemTemplate inside an MvxItemList, then anything in the hierarchy above and including the MvxItemlist cannot have android:layout_height="wrap_content". The reason is that the Android OS has to draw things more than once if it has to determine their height dynamically. All the redraws get things confused in the binding. If you set everything to a fixed height, everything works fine. To fix the above problem, the Markup for the MvxItemView above should be

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:orientation="vertical">
        <MvxListView
            android:layout_width="fill_parent"
            android:layout_height="300dp"
            local:MvxBind="ItemsSource ShipmentLots.Lots"
            local:MvxItemTemplate="@layout/inventorylotview" />
        <ImageButton
            android:src="@drawable/ic_action_new"
            android:layout_width="60dp"
            android:layout_height="60dp"
            android:layout_gravity="center"
            local:MvxBind="Click NewLot_Clicked"
            android:id="@+id/btnLotNew" />
    </LinearLayout>

The key seems to be to arrange your markup so it doesn't have to pre-render the MvxItemList in order to determine the height of a section of the screen. You can refer to this if you want to see more:

https://github.com/MvvmCross/MvvmCross/issues/944

I also have a working example of how to do a MvxSpinner inside an MvxItemList at:

https://github.com/JimWilcox3/MvxSpinnerTest

This started as repo to demonstrate the bug. Once @Cheesebaron showed me what was wrong, I corrected it so it is a working example. Hopefully this will help someone.

Upvotes: 1

Related Questions