3

I am looking to implement the use of an MvxExpandableListView within a Xamarin.Android app that I am writing. I am also using MvvmCross. I can get the ListView to display the list of headers but the app blows up when the user tries to expand the header.

Layout file of View

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:local="http://schemas.android.com/apk/res-auto"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">
    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/backgroundAssignmentTasks"
        android:alpha="0.7"
        android:src="@drawable/background" />
    <Mvx.MvxExpandableListView
        android:id="@+id/listAssignmentTasks"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        android:layout_margin="16dp"
        android:dividerHeight="7dp"
        android:divider="@drawable/list_divider"
        local:GroupItemTemplate="@layout/list_assignment_header"
        local:MvxItemTemplate="@layout/list_assignment_detail"
        local:MvxBind="ItemsSource Tasks; ItemClick AssignmentSelectedCommand" />
</RelativeLayout>

Layout file of the header

<?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-auto"
    android:id="@+id/layoutTaskName"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/specific_lightgrey">
    <TextView
        android:id="@+id/assignmentTaskName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="@android:color/black"
        android:textSize="18dp"
        android:layout_marginLeft="32dp"
        android:layout_marginTop="8dp"
        android:layout_marginBottom="8dp"
        local:MvxBind="Text Title" />
</LinearLayout>

Layout of the detail

<?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-auto"
    android:id="@+id/layoutTaskDescription"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@color/specific_darkgrey"
    android:visibility="visible">
    <TextView
        android:id="@+id/assignmentTaskDescription"
        android:layout_width="match_parent"
        android:layout_height="70dp"
        android:textColor="@android:color/black"
        android:textSize="18dp"
        android:layout_margin="8dp"
        android:lines="4"
        local:MvxBind="Text Description" />
    <Button
        android:layout_width="120dp"
        android:layout_height="wrap_content"
        android:layout_margin="8dp"
        android:layout_gravity="center"
        android:id="@+id/buttonRecordVideo"
        android:textColor="@android:color/white"
        android:textSize="18dp"
        android:text="Record"
        android:background="@color/specific_red" />
    </LinearLayout>

My ViewModel provides the following list

    private List<Assignment> _tasks;
    public List<Assignment> Tasks
    {
        get { return _tasks; }
        set {
            _tasks = value;
            RaisePropertyChanged ("Tasks");
        }
    }

The exception thrown is

[MonoDroid] UNHANDLED EXCEPTION:
[MonoDroid] System.ArgumentNullException: Argument cannot be null.
[MonoDroid] Parameter name: source
[MonoDroid] at System.Linq.Check.Source (object) <IL 0x00010, 0x0005f>
[MonoDroid] at System.Linq.Enumerable.Cast<object> (System.Collections.IEnumerable) <0x0002b>
[MonoDroid] at Cirrious.MvvmCross.Binding.Droid.Views.MvxExpandableListAdapter.GetChildrenCount (int) <IL 0x0000c, 0x000e7>
[MonoDroid] at Android.Widget.IExpandableListAdapterInvoker.n_GetChildrenCount_I (intptr,intptr,int) [0x00009] in /Users/builder/data/lanes/1879/5f55a9ef/source/monodroid/src/Mono.Android/platforms/android-19/src/generated/Android.Widget.IExpandableListAdapter.cs:295
[MonoDroid] at (wrapper dynamic-method) object.4fad6314-d1a6-41b1-be64-97483d3976ab (intptr,intptr,int) <IL 0x00017, 0x0002f>

This leaves me to believe that I need to write an implementation of MvxExpandableListAdapter.

How do I link this to my ViewModel and/or layout files?

JDibble
  • 744
  • 1
  • 8
  • 25

2 Answers2

1

Its look like yours "Tasks" property its null when you inflate layout. Make sure that assign for example in the constructor value to _tasks.

public ViewModelCtor() {
    _tasks = new List<Assignment>();
}
  • The Init method populates the Tasks property, reading the data via a service from a SQLite database. This data shows in the "header" so cannot be null. The ViewModel is performing fine within a released and approved iOS app. – JDibble Jul 17 '15 at 07:58
  • Any one found a fix for this – Sanath Shetty Sep 19 '16 at 08:53
1

The ItemsSource property of the MvxExpandableListView binds to a group of items. When you expand a group, it expects a list of items of that group.

It tries to cast the group to an Enumerable. That fails, because your Assignment class is not a list.

What you could do is inherit from IEnumerable like this:

public class Assignment : IEnumerable<Assignment>
{

    public IEnumerator<Assignment> GetEnumerator()
    {
        return (new List<Assignment> { this }).GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

This way, when you expand, it will show you just one item below your header. But the MvxExpandableListView is actually used for showing a list within a group instead of a collapsible header/detail view.

I think what you really want is a view that expands and shows description and record button. I had exactly the same issue and ended up with this.

  • Use a simple MvxListView
  • Your ItemTemplate will contain the description and record button
  • When the user taps the title, the description and button appear/disappear

In your view layout:

<Mvx.MvxListView
        android:id="@+id/listAssignmentTasks"
        local:MvxBind="ItemsSource CommentList.Result"
        local:MvxItemTemplate="@layout/list_assignment_item"
        local:MvxBind="ItemsSource Tasks"
         />

The list_assignment_item template:

<?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-auto"
    android:id="@+id/layoutTaskName" ...>
  <TextView
      android:id="@+id/assignmentTaskName"
      ...
      local:MvxBind="Text Title; Click ToggleDetails" />
  <TextView
        android:id="@+id/assignmentTaskDescription"
        ...
        local:MvxBind="Text Description; Click ToggleDetails; Visibility DetailsVisible, Converter=Visibility" />
  <Button
      android:id="@+id/buttonRecordVideo"
      ...
      local:MvxBind="Visibility DetailsVisible, Converter=Visibility" />
</LinearLayout>

In your Assignment viewmodel, you create the DetailsVisible property and ToggleDetails command:

public class Assignment
{
    private bool detailsVisible;
    /*....*/
    public bool DetailsVisible
    {
        get
        {
            return this.detailsVisible;
        }
        set
        {
            this.detailsVisible = value;
            this.RaisePropertyChanged(() => DetailsVisible);
        }
    }

    public void ToggleDetails()
    {
        this.DetailsVisible = !this.DetailsVisible;
    }
}

And a visibility converter

public class VisibilityValueConverter : MvxValueConverter<bool>
{
    protected override object Convert(bool value, Type targetType, object parameter, CultureInfo culture)
    {
        return value ? ViewStates.Visible : ViewStates.Gone;
    }
}
Joris Heus
  • 221
  • 1
  • 7
  • Thanks for the response, just back from a 3 week holiday, once Im back into the groove, i will try to implement your solution. Thanks. – JDibble Aug 19 '15 at 08:33