Question

I'm trying to bind a MvxBindableListView in TwoWay mode, for it to update in the View when I Set it's value in the ViewModel (through a Buttons's Click command). Currently it only updates when the layout is fully loaded at start/tabchange...

The ViewModel is:

public List<MyType> TestList
        {
            get { return _testList; }
            set
            {
                _testList = value;
                FirePropertyChanged("TestList");
            }
        }

The .axml in the View is:

<Mvx.MvxBindableListView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        local:MvxBind="{'ItemsSource':{'Path':'TestList','Mode':'TwoWay'}}"
        local:MvxItemTemplate="@layout/my_item_layout" />
Was it helpful?

Solution

The way data-binding works is through an interface called INotifyPropertyChanged

What happens in this interface is that the ViewModel sends the View a message whenever a property changes - e.g.

    FirePropertyChanged("TestList");

With a list, this doesn't help if the contents of the list itself change - e.g. when the list has an item added or removed.


To solve this, the .Net Mvvm implementation includes another interface INotifyCollectionChanged.

A collection - such as a list - can implement INotifyCollectionChanged in order to let the View know when the contents of the collection change.

For example, the collection might fire events containing hints such as:

  • everything has changed - NotifyCollectionChangedAction.Reset
  • an item has been added - NotifyCollectionChangedAction.Add
  • an item has been removed - NotifyCollectionChangedAction.Remove
  • ...

There's a short introduction into this interface about 12:30 into the MvvmCross Xaminar http://www.youtube.com/watch?v=jdiu_dH3z5k

Xaminar


To use this interface for a small in-memory list - e.g. less than 1000 'small' objects - all you have to do is to change your List<T> for an ObservableCollection<T> - the ObservableCollection is a class from the core .Net libraries (from Microsoft or Mono) and it will fire the correct events when you Add/Remove list items.

You can see the source for the Mono ObservableCollection implementation in: https://github.com/mosa/Mono-Class-Libraries/blob/master/mcs/class/System/System.Collections.ObjectModel/ObservableCollection.cs - it is worth taking some time to look at this implementation so that you can understand a bit more about how Mvvm works with INotifyCollectionChanged.

If you use the ObservableCollection class, then your code will become:

    private ObservableCollection<MyType> _testList;
    public ObservableCollection<MyType> TestList
    {
        get { return _testList; }
        set
        {
            _testList = value;
            FirePropertyChanged("TestList");
            // in vNext use RaisePropertyChanged(() => TestList);
        }
    }

with:

 <Mvx.MvxBindableListView
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    local:MvxBind="{'ItemsSource':{'Path':'TestList'}}"
    local:MvxItemTemplate="@layout/my_item_layout" />

Note:

  • that the binding is OneWay - this means that binding is still only going from ViewModel to View - there are no updates going from View to ViewModel.
  • that ObservableCollection is designed to be single-threaded - so make sure all changes to the collection are done on the UI thread - not on a worker thread. If you need to, you can marshall work back onto the UI thread using InvokeOnMainThread(() => { /* do work here */ }) in a ViewModel.
  • that in Android, the way lists work (through the base AdapterView) means that every time you call any update on the ObservableCollection then the UI List will ignore the action hint (Add, Remove, etc) - it will treat every change as a Reset and this will cause the entire list to redraw.

For larger collections - where you don't want all the items in memory at the same time - you may need to implement some data-store backed list yourself.

There is a brief example of one simple sqlite data-backed store in https://github.com/slodge/MvvmCross/blob/vnext/Sample%20-%20SimpleDialogBinding/SimpleDroidSql.Core/DatabaseBackedObservableCollection.cs

This virtualizing of collection data is common in WP and WPF apps - e.g. see questions and answers like Is listbox virtualized by default in WP7 Mango?

OTHER TIPS

We just found a workaround for this that works for us!!

NOTE: Adding and removing from the list updates the view with the new/removed item. However any changes to the state of the existing items were not reflected.

SOLUTION: We cleared our list and re-added the items to the ViewModel property with the updated state. Calling raisepropertychanged then mimics a two-way bind behavior. Essentially it was removing all values and re-adding all values.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top